Authorization Failures Alarm Should Be Enabled
More Info:
Any unauthorized API calls made within your AWS account 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
- HIPAA
- HITRUST CSF
- ISO 27001
- ISO/IEC 27017
- ISO/IEC 27018
- ISO/IEC 27701
- KSA PDPL
- MAS Technology Risk Management (Singapore)
- MITRE ATT&CK (Cloud)
- NIS2 Directive
- NIST SP 800-171
- NYDFS 23 NYCRR 500
- 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 the console steps to enable an “Authorization Failures” alarm in CloudWatch based on CloudTrail logs.
Prerequisites
- You already have an AWS CloudTrail trail sending events to a CloudWatch Logs log group.
1. Confirm / Set CloudTrail Log Group
- In the AWS Console, go to CloudTrail.
- In the left menu, choose Trails.
- Click your active trail.
- Under CloudWatch Logs, confirm:
- CloudWatch Logs log group is set (for example:
/aws/cloudtrail/your-trail). - If not configured:
- Click Edit (or Configure).
- Enable Send to CloudWatch Logs.
- Select / create a Log group.
- Select / create an IAM role if prompted.
- Save changes and wait a few minutes for logs to start streaming.
- CloudWatch Logs log group is set (for example:
2. Create a Metric Filter for Authorization Failures
-
Go to CloudWatch in the console.
-
In the left menu, select Logs → Log groups.
-
Click the CloudTrail log group (e.g.,
/aws/cloudtrail/your-trail). -
Go to the Metric filters tab and click Create metric filter.
-
In Filter pattern, use a pattern that matches authorization errors, for example:
{ ($.errorCode = "*UnauthorizedOperation") || ($.errorCode = "AccessDenied*") } -
Click Next.
-
For Assign metric, fill in:
- Filter name:
AuthorizationFailuresFilter - Metric namespace:
CIS/CloudTrail(or any custom namespace) - Metric name:
AuthorizationFailures - Metric value:
1 - Default value (optional):
0
- Filter name:
-
Click Next, then Create metric filter.
3. Create a CloudWatch Alarm on the Metric
- Still in CloudWatch, go to Alarms → All alarms.
- Click Create alarm.
- Click Select metric.
- Navigate to Custom namespaces → your namespace (e.g.,
CIS/CloudTrail) → select theAuthorizationFailuresmetric. - Click Select metric.
- Set Statistic to
Sumand choose a Period (e.g.,5 minutes). - Define the condition:
- Threshold type: Static
- Whenever Sum is:
Greater than - Threshold:
0
- Click Next.
4. Configure Notification (SNS)
- In the Notification section:
- Under Alarm state trigger, select In alarm.
- Choose an existing SNS topic or click Create new topic.
- If creating a new topic:
- Provide a name (e.g.,
AuthorizationFailuresTopic). - Enter one or more email addresses.
- Provide a name (e.g.,
- After creation, confirm subscription from the email(s) you receive.
- Click Next.
5. Name and Create the Alarm
- Give the alarm a name and description, for example:
- Name:
AuthorizationFailuresAlarm - Description:
Alarm when CloudTrail records authorization failures (AccessDenied / UnauthorizedOperation).
- Name:
- Review all settings.
- Click Create alarm.
Once done, any CloudTrail event that matches the filter (authorization failures) will increment the metric, and if it exceeds the threshold (>0 in the period), the CloudWatch alarm will enter ALARM state and trigger your SNS notification.
Using CLI
Below are concise, step‑by‑step AWS CLI instructions to set up a CloudWatch alarm for authorization failures (e.g., AuthorizationFailure, AccessDenied) from CloudTrail logs.
Assumptions:
- You already have CloudTrail sending logs to a CloudWatch Logs log group.
- Replace all ALL_CAPS placeholders with your values.
1. Identify your CloudTrail log group
If you don’t know it:
aws logs describe-log-groups --query "logGroups[].logGroupName"
Pick the CloudTrail log group name, for example:
/aws/cloudtrail/your-account-trails
Set it in a variable (optional but convenient):
LOG_GROUP_NAME="/aws/cloudtrail/your-account-trails"
2. Create a metric filter for authorization failures
Filter pattern to catch common authorization failures:
FILTER_NAME="AuthorizationFailures"
METRIC_NAMESPACE="SecurityMetrics"
METRIC_NAME="AuthorizationFailuresCount"
aws logs put-metric-filter \
--log-group-name "$LOG_GROUP_NAME" \
--filter-name "$FILTER_NAME" \
--filter-pattern '{ ($.errorCode = "*UnauthorizedOperation") || ($.errorCode = "AccessDenied*") || ($.errorCode = "AuthorizationFailure") }' \
--metric-transformations \
metricName="$METRIC_NAME",metricNamespace="$METRIC_NAMESPACE",metricValue=1
Verify:
aws logs describe-metric-filters \
--log-group-name "$LOG_GROUP_NAME" \
--filter-name-prefix "$FILTER_NAME"
3. Create/choose an SNS topic for the alarm notification
Create topic:
SNS_TOPIC_ARN=$(aws sns create-topic --name security-authorization-failures --query 'TopicArn' --output text)
Subscribe your email (or another endpoint):
aws sns subscribe \
--topic-arn "$SNS_TOPIC_ARN" \
--protocol email \
--notification-endpoint YOUR_EMAIL@example.com
Confirm the subscription from your email inbox.
4. Create the CloudWatch alarm on the metric
Example: alarm if ≥ 1 authorization failure in 5 minutes.
ALARM_NAME="AuthorizationFailuresAlarm"
aws cloudwatch put-metric-alarm \
--alarm-name "$ALARM_NAME" \
--alarm-description "Alarm when AWS CloudTrail logs record authorization failures" \
--namespace "$METRIC_NAMESPACE" \
--metric-name "$METRIC_NAME" \
--statistic Sum \
--period 300 \
--evaluation-periods 1 \
--threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold \
--treat-missing-data notBreaching \
--alarm-actions "$SNS_TOPIC_ARN"
Check the alarm:
aws cloudwatch describe-alarms --alarm-names "$ALARM_NAME"
5. Test the alarm (optional)
- Intentionally perform an AWS action your IAM user/role is not allowed to do (in a safe, non‑prod way).
- Wait a few minutes; the alarm should go into
ALARMstate and send an SNS notification.
This completes enabling an “Authorization Failures” alarm using AWS CLI.
Using Python
Below are step‑by‑step remediation instructions and example Python (boto3) code to ensure an “Authorization Failures” alarm is enabled in AWS using CloudWatch.
Assumptions:
- You have:
- A CloudTrail trail logging to a CloudWatch Logs log group (e.g.
/aws/cloudtrail/logs) - An IAM principal with permissions for
logs:*,cloudwatch:*,iam:*, andcloudtrail:*.
- A CloudTrail trail logging to a CloudWatch Logs log group (e.g.
1. Decide what to alarm on
Common pattern: alarm on CloudTrail events where API calls fail with:
errorCode = "AccessDenied*"- OR
errorCode = "UnauthorizedOperation"
We’ll:
- Create a CloudWatch Logs Metric Filter on the CloudTrail log group.
- Create a CloudWatch Alarm on that metric.
- (Optional) Wire it into an SNS topic for notifications.
2. Create the CloudWatch Logs Metric Filter (Python)
This metric filter will increment a metric every time an “authorization failure” is seen.
import boto3
logs_client = boto3.client('logs', region_name='us-east-1')
LOG_GROUP_NAME = '/aws/cloudtrail/logs' # replace with your CloudTrail log group
METRIC_NAMESPACE = 'Security/Authorization'
METRIC_NAME = 'AuthorizationFailures'
FILTER_NAME = 'AuthorizationFailuresFilter'
# Filter pattern for access denied / unauthorized operations from CloudTrail logs
FILTER_PATTERN = (
'{ ($.errorCode = "AccessDenied*" || $.errorCode = "UnauthorizedOperation") && '
'($.eventSource != "signin.amazonaws.com") }'
)
def create_metric_filter():
logs_client.put_metric_filter(
logGroupName=LOG_GROUP_NAME,
filterName=FILTER_NAME,
filterPattern=FILTER_PATTERN,
metricTransformations=[
{
'metricName': METRIC_NAME,
'metricNamespace': METRIC_NAMESPACE,
'metricValue': '1',
'defaultValue': 0.0
}
]
)
print(f"Created/updated metric filter '{FILTER_NAME}' on log group '{LOG_GROUP_NAME}'")
if __name__ == '__main__':
create_metric_filter()
Notes:
- Adjust
LOG_GROUP_NAMEto your actual CloudTrail CloudWatch Logs group. - Adjust
region_nameas needed.
3. Create an SNS Topic for Alarm Notifications (optional but recommended)
import boto3
sns_client = boto3.client('sns', region_name='us-east-1')
TOPIC_NAME = 'AuthorizationFailuresAlarmTopic'
EMAIL_SUBSCRIPTION = 'you@example.com' # change to your email
def create_sns_topic_and_subscription():
# Create or get SNS topic
topic_response = sns_client.create_topic(Name=TOPIC_NAME)
topic_arn = topic_response['TopicArn']
print(f"SNS topic ARN: {topic_arn}")
# Subscribe email to topic
sns_client.subscribe(
TopicArn=topic_arn,
Protocol='email',
Endpoint=EMAIL_SUBSCRIPTION
)
print(f"Subscription request sent to {EMAIL_SUBSCRIPTION}. Confirm via email.")
return topic_arn
if __name__ == '__main__':
create_sns_topic_and_subscription()
4. Create the CloudWatch Alarm on This Metric (Python)
This alarm triggers when more than a certain number of authorization failures occur in a given time window.
import boto3
cloudwatch_client = boto3.client('cloudwatch', region_name='us-east-1')
METRIC_NAMESPACE = 'Security/Authorization'
METRIC_NAME = 'AuthorizationFailures'
ALARM_NAME = 'AuthorizationFailuresAlarm'
ALARM_DESCRIPTION = 'Alarm when AWS API calls fail due to authorization failures'
EVALUATION_PERIODS = 1 # number of evaluation periods
PERIOD = 300 # 300 seconds = 5 minutes
THRESHOLD = 1 # alarm if >= 1 event in the period
STATISTIC = 'Sum'
COMPARISON_OPERATOR = 'GreaterThanOrEqualToThreshold'
SNS_TOPIC_ARN = 'arn:aws:sns:us-east-1:123456789012:AuthorizationFailuresAlarmTopic' # replace with your ARN
def create_authorization_failures_alarm():
cloudwatch_client.put_metric_alarm(
AlarmName=ALARM_NAME,
AlarmDescription=ALARM_DESCRIPTION,
Namespace=METRIC_NAMESPACE,
MetricName=METRIC_NAME,
Statistic=STATISTIC,
Period=PERIOD,
EvaluationPeriods=EVALUATION_PERIODS,
Threshold=THRESHOLD,
ComparisonOperator=COMPARISON_OPERATOR,
TreatMissingData='notBreaching',
AlarmActions=[SNS_TOPIC_ARN], # can be empty list if no action required
OKActions=[SNS_TOPIC_ARN], # optional: notify when alarm returns to OK
ActionsEnabled=True
)
print(f"Created/updated CloudWatch alarm '{ALARM_NAME}'")
if __name__ == '__main__':
create_authorization_failures_alarm()
Adjust:
SNS_TOPIC_ARNto the ARN from step 3 or omitAlarmActionsif you don’t want notifications.THRESHOLD,PERIOD,EVALUATION_PERIODSbased on your sensitivity to alerts.
5. Verify the Alarm
-
In the AWS Console:
- Go to CloudWatch → Logs → Log groups: confirm the metric filter exists.
- Go to CloudWatch → Metrics → Security/Authorization: confirm the metric is visible after some denied API calls.
- Go to CloudWatch → Alarms: verify
AuthorizationFailuresAlarmisOKand configured with the correct metric and SNS action.
-
Generate a test authorization failure (e.g., call an API without required permissions) and confirm:
- Metric increments.
- Alarm moves to
ALARMstate when threshold is crossed. - Notification is sent (if SNS configured).
If you tell me:
- Your region
- Your CloudTrail log group name I can adapt the code snippets exactly to your environment.
Using Terraform
# CloudTrail log group that receives your CloudTrail events
# Replace LOG_GROUP_NAME with your actual CloudTrail CloudWatch Logs group name
resource "aws_cloudwatch_log_group" "cloudtrail" {
name = "CLOUDTRAIL_LOG_GROUP_NAME" # <-- replace with your log group name
}
# Metric filter for authorization failures (UnauthorizedOperation / AccessDenied*)
resource "aws_cloudwatch_log_metric_filter" "authorization_failures" {
name = "AuthorizationFailuresFilter"
log_group_name = aws_cloudwatch_log_group.cloudtrail.name
# Matches the verified CLI filter-pattern exactly
pattern = "{($.errorCode = \"*UnauthorizedOperation\") || ($.errorCode = \"AccessDenied*\")}"
metric_transformation {
name = "AuthorizationFailures"
namespace = "CloudTrailMetrics"
value = "1"
}
}
# CloudWatch alarm on the metric created by the filter
resource "aws_cloudwatch_metric_alarm" "authorization_failures" {
alarm_name = "AuthorizationFailuresAlarm"
alarm_description = "Triggers when 3 or more API authorization failures are detected in 5 minutes"
namespace = aws_cloudwatch_log_metric_filter.authorization_failures.metric_transformation[0].namespace
metric_name = aws_cloudwatch_log_metric_filter.authorization_failures.metric_transformation[0].name
statistic = "Sum"
period = 300
threshold = 3
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 1
alarm_actions = [
"SNS_TOPIC_ARN", # <-- replace with your SNS topic ARN for notifications
]
depends_on = [
aws_cloudwatch_log_metric_filter.authorization_failures,
]
}
Running terraform plan should show creation of aws_cloudwatch_log_metric_filter.authorization_failures and aws_cloudwatch_metric_alarm.authorization_failures (and the log group if it is not already managed in Terraform), with the metric name, namespace, pattern, threshold, period, and alarm name matching the CLI remediation.