Skip to main content

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

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

  1. In the AWS Console, go to CloudTrail.
  2. In the left menu, choose Trails.
  3. Click your active trail.
  4. 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.

2. Create a Metric Filter for Authorization Failures

  1. Go to CloudWatch in the console.

  2. In the left menu, select Logs → Log groups.

  3. Click the CloudTrail log group (e.g., /aws/cloudtrail/your-trail).

  4. Go to the Metric filters tab and click Create metric filter.

  5. In Filter pattern, use a pattern that matches authorization errors, for example:

    { ($.errorCode = "*UnauthorizedOperation") || ($.errorCode = "AccessDenied*") }
  6. Click Next.

  7. 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
  8. Click Next, then Create metric filter.


3. Create a CloudWatch Alarm on the Metric

  1. Still in CloudWatch, go to Alarms → All alarms.
  2. Click Create alarm.
  3. Click Select metric.
  4. Navigate to Custom namespaces → your namespace (e.g., CIS/CloudTrail) → select the AuthorizationFailures metric.
  5. Click Select metric.
  6. Set Statistic to Sum and choose a Period (e.g., 5 minutes).
  7. Define the condition:
    • Threshold type: Static
    • Whenever Sum is: Greater than
    • Threshold: 0
  8. Click Next.

4. Configure Notification (SNS)

  1. 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.
    • After creation, confirm subscription from the email(s) you receive.
  2. Click Next.

5. Name and Create the Alarm

  1. Give the alarm a name and description, for example:
    • Name: AuthorizationFailuresAlarm
    • Description: Alarm when CloudTrail records authorization failures (AccessDenied / UnauthorizedOperation).
  2. Review all settings.
  3. 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 ALARM state 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:*, and cloudtrail:*.

1. Decide what to alarm on

Common pattern: alarm on CloudTrail events where API calls fail with:

  • errorCode = "AccessDenied*"
  • OR errorCode = "UnauthorizedOperation"

We’ll:

  1. Create a CloudWatch Logs Metric Filter on the CloudTrail log group.
  2. Create a CloudWatch Alarm on that metric.
  3. (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_NAME to your actual CloudTrail CloudWatch Logs group.
  • Adjust region_name as needed.

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_ARN to the ARN from step 3 or omit AlarmActions if you don’t want notifications.
  • THRESHOLD, PERIOD, EVALUATION_PERIODS based on your sensitivity to alerts.

5. Verify the Alarm

  1. 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 AuthorizationFailuresAlarm is OK and configured with the correct metric and SNS action.
  2. Generate a test authorization failure (e.g., call an API without required permissions) and confirm:

    • Metric increments.
    • Alarm moves to ALARM state 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.

Additional Reading: