S3 Bucket Changes Alarm Should Be Enabled
More Info:
AWS S3 Buckets configuration changes should be monitored using CloudWatch alarms.
Risk Level
Medium
Address
Security
Compliance Standards
- APRA CPS 234 (Australia)
- AWS Well Architected Framework
- BSI C5 (Germany)
- Brazil LGPD
- CCPA / CPRA (California)
- CIS AWS
- CIS Critical Security Controls v8
- CMMC 2.0
- CSA Cloud Controls Matrix v4
- Cloudanix Best Practice
- DPDPA
- Digital Operational Resilience Act (EU)
- Essential 8
- HITRUST CSF
- ISO/IEC 27017
- ISO/IEC 27018
- ISO/IEC 27701
- KSA PDPL
- MAS Technology Risk Management (Singapore)
- MITRE ATT&CK (Cloud)
- NIS2 Directive
- NIST
- NIST CSF
- NIST SP 800-171
- NYDFS 23 NYCRR 500
- PCI
- SOC2
- SWIFT Customer Security Controls Framework
- Sarbanes-Oxley IT General Controls
- UK NCSC Cyber Assessment Framework
Triage and Remediation
- Remediation
Remediation
Using Console
Below are concise, console-based steps to enable an S3 bucket changes alarm using CloudWatch and CloudTrail.
1. Ensure CloudTrail Is Enabled and Logging to CloudWatch Logs
- In the AWS Console, go to CloudTrail.
- In the left menu, select Trails.
- If you already have a trail:
- Click on the trail name.
- Under CloudWatch Logs, confirm:
- CloudWatch Logs log group is set.
- An IAM role is attached for CloudTrail to send logs to CloudWatch Logs.
- If not set, click Edit, then:
- Set CloudWatch Logs to Enabled.
- Choose or create a Log group.
- Choose or create a suitable IAM role.
- Save changes.
- If you do not have a trail:
- Click Create trail.
- Name the trail.
- For Storage location, choose/create an S3 bucket.
- For CloudWatch Logs, set to Enabled, and pick/create a log group and IAM role.
- Ensure Management events (Read and Write) are enabled.
- Create the trail.
2. Create a Metric Filter for S3 Bucket Changes
You will filter CloudTrail events that represent S3 bucket changes (such as PutBucketAcl, PutBucketPolicy, etc.).
-
Go to CloudWatch in the console.
-
In the left menu, select Logs → Log groups.
-
Click the log group used by CloudTrail (e.g.,
/aws/cloudtrail/logs). -
Choose the Metric filters tab.
-
Click Create metric filter (or Add metric filter).
-
In Filter pattern, use something like:
{ ($.eventSource = "s3.amazonaws.com") &&($.eventName = "PutBucketAcl" ||$.eventName = "PutBucketPolicy" ||$.eventName = "PutBucketCors" ||$.eventName = "PutBucketLifecycle" ||$.eventName = "PutBucketReplication" ||$.eventName = "PutBucketVersioning" ||$.eventName = "PutBucketWebsite" ||$.eventName = "DeleteBucketPolicy" ||$.eventName = "DeleteBucketCors" ||$.eventName = "DeleteBucketLifecycle" ||$.eventName = "DeleteBucketReplication" ||$.eventName = "DeleteBucketWebsite") }(Adjust the event list to your policy as needed.)
-
Click Next.
-
Under Assign metric, set:
- Filter name: e.g.,
S3BucketChangesFilter. - Metric namespace: e.g.,
Security/CloudTrail. - Metric name: e.g.,
S3BucketChanges. - Metric value:
1.
- Filter name: e.g.,
-
Click Next, then Create metric filter.
3. Create a CloudWatch Alarm for S3 Bucket Changes
- In CloudWatch, go to Alarms → All alarms.
- Click Create alarm.
- Click Select metric.
- Choose Browse → go to the namespace you set (e.g.,
Security/CloudTrail) → select the S3BucketChanges metric. - Click Select metric.
- Configure the alarm:
- Statistic:
Sum. - Period: e.g.,
5 minutes(or as required). - Threshold type:
Static. - Condition:
Greater/Equalto1.
- Statistic:
- Click Next.
- Under Notification:
- Select an existing SNS topic or click Create new topic.
- Enter an email endpoint or other subscribers as needed.
- Click Next, give the alarm a name, e.g.,
Alarm-S3BucketChanges, and optional description. - Click Next, then Create alarm.
Once complete, whenever S3 bucket configuration changes (covered by your filter) occur, CloudTrail logs them, the metric filter increments the S3BucketChanges metric, and the CloudWatch alarm triggers and sends a notification.
Using CLI
Below is one concrete way to implement an “S3 bucket changes” CloudWatch alarm using AWS CLI:
Goal: Alarm whenever someone changes S3 bucket configuration, such as ACLs/policies.
This uses CloudTrail → CloudWatch Logs → Metric Filter → CloudWatch Alarm.
0. Prerequisites
- You have an S3 bucket with CloudTrail logs (or will create a new trail).
- You have (or will create) a CloudWatch Logs log group for CloudTrail.
- You have an SNS topic ARN to send the alarm notification to (or will create one).
I’ll include all commands.
1. Create / Configure CloudTrail to Send to CloudWatch Logs
# Variables
REGION="us-east-1"
TRAIL_NAME="organization-trail" # or your desired name
CLOUDTRAIL_BUCKET="my-cloudtrail-bucket-123"
LOG_GROUP_NAME="/aws/cloudtrail/s3-bucket-changes"
ROLE_NAME="CloudTrail_CloudWatchLogs_Role"
ROLE_POLICY_NAME="CloudTrail_CloudWatchLogs_Policy"
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
1.1 Create the CloudWatch Logs log group (if not exists)
aws logs create-log-group \
--log-group-name "$LOG_GROUP_NAME" \
--region "$REGION" 2>/dev/null || true
1.2 Create IAM role for CloudTrail to put logs into CloudWatch Logs
Trust policy:
cat > trust-policy.json <<EOF
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "cloudtrail.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}
EOF
aws iam create-role \
--role-name "$ROLE_NAME" \
--assume-role-policy-document file://trust-policy.json
Permissions policy:
cat > cwlogs-policy.json <<EOF
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:${ACCOUNT_ID}:log-group:${LOG_GROUP_NAME}:*"
}]
}
EOF
aws iam put-role-policy \
--role-name "$ROLE_NAME" \
--policy-name "$ROLE_POLICY_NAME" \
--policy-document file://cwlogs-policy.json
1.3 Create (or update) the CloudTrail trail
# Create S3 bucket for CloudTrail if needed
aws s3api create-bucket \
--bucket "$CLOUDTRAIL_BUCKET" \
--region "$REGION" \
--create-bucket-configuration LocationConstraint="$REGION" 2>/dev/null || true
# Create the trail
aws cloudtrail create-trail \
--name "$TRAIL_NAME" \
--s3-bucket-name "$CLOUDTRAIL_BUCKET" \
--cloud-watch-logs-log-group-arn "arn:aws:logs:${REGION}:${ACCOUNT_ID}:log-group:${LOG_GROUP_NAME}" \
--cloud-watch-logs-role-arn "arn:aws:iam::${ACCOUNT_ID}:role/${ROLE_NAME}" \
--is-multi-region-trail
# Start logging
aws cloudtrail start-logging --name "$TRAIL_NAME"
2. Create a Metric Filter for S3 Bucket Changes
We’ll match CloudTrail events that modify bucket configuration (common examples: PutBucketAcl, PutBucketPolicy, PutBucketCors, PutBucketLifecycleConfiguration, etc.).
2.1 Define the metric filter
METRIC_FILTER_NAME="S3BucketChangesFilter"
METRIC_NAMESPACE="Security/S3"
METRIC_NAME="S3BucketChangesCount"
FILTER_PATTERN='{ ($.eventSource = "s3.amazonaws.com") && (
$.eventName = "PutBucketAcl" ||
$.eventName = "PutBucketPolicy" ||
$.eventName = "PutBucketCors" ||
$.eventName = "PutBucketLifecycleConfiguration" ||
$.eventName = "PutBucketReplication" ||
$.eventName = "PutBucketVersioning" ||
$.eventName = "PutBucketLogging" ||
$.eventName = "PutBucketEncryption" ||
$.eventName = "PutBucketPublicAccessBlock" ||
$.eventName = "DeleteBucketPolicy" ||
$.eventName = "DeleteBucketCors" ||
$.eventName = "DeleteBucketLifecycle" ||
$.eventName = "DeleteBucketReplication" ||
$.eventName = "DeleteBucketWebsite"
)}'
2.2 Create the metric filter
aws logs put-metric-filter \
--log-group-name "$LOG_GROUP_NAME" \
--filter-name "$METRIC_FILTER_NAME" \
--filter-pattern "$FILTER_PATTERN" \
--metric-transformations \
metricName="$METRIC_NAME",metricNamespace="$METRIC_NAMESPACE",metricValue=1
3. Create an SNS Topic for Alarm Notifications (if needed)
SNS_TOPIC_NAME="S3BucketChangesAlarmTopic"
SNS_TOPIC_ARN=$(aws sns create-topic \
--name "$SNS_TOPIC_NAME" \
--query 'TopicArn' \
--output text)
# Subscribe an email address
aws sns subscribe \
--topic-arn "$SNS_TOPIC_ARN" \
--protocol email \
--notification-endpoint you@example.com
(You must confirm the subscription email.)
4. Create the CloudWatch Alarm
Alarm if any bucket-change event occurs in a 5‑minute period.
ALARM_NAME="S3BucketChangesAlarm"
aws cloudwatch put-metric-alarm \
--alarm-name "$ALARM_NAME" \
--alarm-description "Alarm when S3 bucket configuration is changed" \
--metric-name "$METRIC_NAME" \
--namespace "$METRIC_NAMESPACE" \
--statistic Sum \
--period 300 \
--evaluation-periods 1 \
--threshold 0 \
--comparison-operator GreaterThanThreshold \
--treat-missing-data notBreaching \
--alarm-actions "$SNS_TOPIC_ARN"
5. Validate
- Make a bucket change (e.g., modify ACL or bucket policy).
- Check:
- CloudTrail logs show the event.
- The metric
Security/S3 : S3BucketChangesCountincreases. - The
S3BucketChangesAlarmmoves toALARMstate and sends an SNS notification.
This fully enables an alarm on S3 bucket configuration changes using CloudWatch and AWS CLI.
Using Python
To detect S3 bucket configuration changes with CloudWatch, you need:
- CloudTrail sending logs to a CloudWatch Logs log group
- A CloudWatch Logs metric filter for S3 bucket-change events
- A CloudWatch alarm on that metric
Below is a step‑by‑step guide plus a Python (boto3) example.
1. Prerequisites
boto3installed and configured with credentials/region:pip install boto3aws configure- An existing CloudTrail trail that:
- Logs management events
- Delivers to a CloudWatch Logs log group (e.g.,
/aws/cloudtrail/main)
If CloudTrail is not yet sending to CloudWatch Logs, configure that first (in console or via boto3.cloudtrail.update_trail with CloudWatchLogsLogGroupArn).
2. Recommended metric filter pattern
This pattern matches S3 bucket configuration changes in CloudTrail logs:
{ ($.eventSource = "s3.amazonaws.com") &&
(
($.eventName = "PutBucketAcl") ||
($.eventName = "PutBucketPolicy") ||
($.eventName = "PutBucketCors") ||
($.eventName = "PutBucketLifecycle") ||
($.eventName = "PutBucketReplication") ||
($.eventName = "PutBucketTagging") ||
($.eventName = "PutBucketVersioning") ||
($.eventName = "PutBucketWebsite") ||
($.eventName = "DeleteBucketPolicy") ||
($.eventName = "DeleteBucketCors") ||
($.eventName = "DeleteBucketLifecycle") ||
($.eventName = "DeleteBucketReplication") ||
($.eventName = "DeleteBucketTagging") ||
($.eventName = "DeleteBucketWebsite")
)
}
3. Python script to create metric filter + alarm
This script will:
- Create/overwrite a metric filter on the specified CloudWatch Logs log group
- Create/overwrite a CloudWatch alarm that fires when ≥ 1 S3 bucket change is detected in a 5‑minute period
import boto3
import json
region = "us-east-1" # change as needed
log_group_name = "/aws/cloudtrail/main" # your existing CloudTrail log group
metric_namespace = "SecurityMonitoring"
metric_name = "S3BucketChanges"
metric_filter_name = "S3BucketChangesFilter"
alarm_name = "S3BucketChangesAlarm"
sns_topic_arn = "arn:aws:sns:us-east-1:123456789012:SecurityAlerts" # optional, set to None if not using SNS
logs_client = boto3.client("logs", region_name=region)
cw_client = boto3.client("cloudwatch", region_name=region)
metric_filter_pattern = (
'{ ($.eventSource = "s3.amazonaws.com") && '
'('
'($.eventName = "PutBucketAcl") || '
'($.eventName = "PutBucketPolicy") || '
'($.eventName = "PutBucketCors") || '
'($.eventName = "PutBucketLifecycle") || '
'($.eventName = "PutBucketReplication") || '
'($.eventName = "PutBucketTagging") || '
'($.eventName = "PutBucketVersioning") || '
'($.eventName = "PutBucketWebsite") || '
'($.eventName = "DeleteBucketPolicy") || '
'($.eventName = "DeleteBucketCors") || '
'($.eventName = "DeleteBucketLifecycle") || '
'($.eventName = "DeleteBucketReplication") || '
'($.eventName = "DeleteBucketTagging") || '
'($.eventName = "DeleteBucketWebsite")'
')'
'}'
)
def create_metric_filter():
print(f"Creating/updating metric filter '{metric_filter_name}'...")
logs_client.put_metric_filter(
logGroupName=log_group_name,
filterName=metric_filter_name,
filterPattern=metric_filter_pattern,
metricTransformations=[
{
"metricName": metric_name,
"metricNamespace": metric_namespace,
"metricValue": "1"
}
]
)
print("Metric filter created/updated.")
def create_alarm():
print(f"Creating/updating alarm '{alarm_name}'...")
alarm_kwargs = {
"AlarmName": alarm_name,
"AlarmDescription": "Alarm when S3 bucket configuration changes are detected via CloudTrail.",
"ActionsEnabled": True,
"MetricName": metric_name,
"Namespace": metric_namespace,
"Statistic": "Sum",
"Period": 300, # 5 minutes
"EvaluationPeriods": 1,
"DatapointsToAlarm": 1,
"Threshold": 1.0,
"ComparisonOperator": "GreaterThanOrEqualToThreshold",
"TreatMissingData": "notBreaching"
}
if sns_topic_arn:
alarm_kwargs["AlarmActions"] = [sns_topic_arn]
alarm_kwargs["OKActions"] = [sns_topic_arn]
cw_client.put_metric_alarm(**alarm_kwargs)
print("Alarm created/updated.")
if __name__ == "__main__":
create_metric_filter()
create_alarm()
print("Remediation complete: S3 Bucket Changes Alarm enabled.")
4. Summary of remediation steps
- Ensure CloudTrail is enabled and sending logs to a CloudWatch Logs log group.
- Use the script (or similar boto3 code) to:
- Add a metric filter on that log group for S3 bucket config‑change events.
- Create a CloudWatch alarm on that metric (with SNS notifications if desired).
Using Terraform
# SNS topic for alarm notifications
resource "aws_sns_topic" "s3_bucket_changes_topic" {
name = "S3BucketChangesTopic"
}
# OPTIONAL: subscribe an email endpoint to the SNS topic
# Replace ALERT_EMAIL_ADDRESS with the destination email (e.g., "user@example.com")
resource "aws_sns_topic_subscription" "s3_bucket_changes_email" {
topic_arn = aws_sns_topic.s3_bucket_changes_topic.arn
protocol = "email"
endpoint = "ALERT_EMAIL_ADDRESS"
}
# CloudWatch Logs metric filter on the CloudTrail log group
# Replace CLOUDTRAIL_LOG_GROUP_NAME with the name of the CloudTrail log group
resource "aws_cloudwatch_log_metric_filter" "s3_bucket_changes_filter" {
name = "S3BucketChangesFilter"
log_group_name = "CLOUDTRAIL_LOG_GROUP_NAME"
# Mirrors the filter-pattern from the verified CLI remediation
pattern = "{($.eventSource=s3.amazonaws.com) && (($.eventName=CreateBucket) || ($.eventName=DeleteBucket) || ($.eventName=PutBucketAcl) || ($.eventName=PutBucketPolicy) || ($.eventName=PutBucketCors) || ($.eventName=PutBucketLifecycle) || ($.eventName=PutBucketReplication) || ($.eventName=PutBucketLogging) || ($.eventName=PutBucketTagging) || ($.eventName=PutBucketWebsite) || ($.eventName=PutBucketVersioning) || ($.eventName=DeleteBucketPolicy) || ($.eventName=DeleteBucketCors) || ($.eventName=DeleteBucketLifecycle) || ($.eventName=DeleteBucketReplication) || ($.eventName=DeleteBucketTagging) || ($.eventName=DeleteBucketWebsite))}"
metric_transformation {
name = "S3BucketChangesMetric"
namespace = "CloudTrailMetrics"
value = "1"
}
}
# CloudWatch alarm for S3 bucket configuration changes
resource "aws_cloudwatch_metric_alarm" "s3_bucket_changes_alarm" {
alarm_name = "S3BucketChangesAlarm"
alarm_description = "Alarm for S3 bucket configuration changes"
namespace = "CloudTrailMetrics"
metric_name = "S3BucketChangesMetric"
statistic = "Sum"
period = 300
threshold = 1
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 1
alarm_actions = [
aws_sns_topic.s3_bucket_changes_topic.arn
]
depends_on = [
aws_cloudwatch_log_metric_filter.s3_bucket_changes_filter
]
}
This assumes you already have a CloudTrail trail delivering management events to the CLOUDTRAIL_LOG_GROUP_NAME CloudWatch Logs group; that trail configuration is managed separately. No resources above require forced replacement beyond standard Terraform create/update behavior.
To verify, terraform plan should show:
+createaws_sns_topic.s3_bucket_changes_topic+createaws_sns_topic_subscription.s3_bucket_changes_email(if kept)+createaws_cloudwatch_log_metric_filter.s3_bucket_changes_filter+createaws_cloudwatch_metric_alarm.s3_bucket_changes_alarm.