This check flags AWS accounts that have no CloudWatch alarm watching for failed console sign-ins. Without it, brute-force attempts against the AWS console go unnoticed. Fix it by creating a metric filter on your CloudTrail log group plus a CloudWatch alarm wired to an SNS topic.
Console sign-in failures are one of the earliest signals that someone is trying to break into your AWS account. A handful of failed logins in a short window usually means one of two things: a teammate fat-fingered their password, or somebody is running a credential-stuffing attack against the root user or an IAM user. The trouble is that AWS does not tell you about it unless you ask. CloudTrail records the events, but nothing watches them for you by default.
The account_alarm_consolefailures check looks for a CloudWatch alarm tied to a metric filter that counts ConsoleLogin events with a failed result. If that alarm does not exist, the check fails. This maps directly to CIS AWS Foundations Benchmark control 3.2.
What this check detects
The check inspects your account for a specific monitoring chain:
- A CloudTrail trail delivering management events to a CloudWatch Logs group.
- A metric filter on that log group matching console authentication failures.
- A CloudWatch alarm based on that metric, pointed at an SNS topic with at least one subscriber.
If any link in that chain is missing, you have no real-time visibility into failed logins. The check passes only when all three pieces are in place and active.
Note: The relevant CloudTrail event is ConsoleLogin in the signin.amazonaws.com event source. A failed attempt carries responseElements.ConsoleLogin = "Failure". This is the field your metric filter keys off.
Why it matters
The AWS console is internet-facing. Anyone who knows your account ID, or guesses an IAM user name, can hit the login page and start trying passwords. Attackers automate this. They spray common passwords across thousands of accounts and wait for a hit. If you have IAM users without MFA, or a root account protected only by a password, this is a live threat, not a theoretical one.
Without an alarm, the timeline of an attack looks like this:
- An attacker runs a brute-force or password-spray attack against your console.
- CloudTrail dutifully logs every failed attempt.
- Nobody looks at CloudTrail until something has already gone wrong.
- The attacker eventually succeeds, or moves on, and you find out weeks later during an audit.
With the alarm in place, you get an SNS notification the moment failures cross your threshold. That gives your on-call engineer a chance to lock the account, force a password reset, or block the source IP before credentials are compromised.
Warning: Failed console logins for the root user deserve their own dedicated, lower-threshold alarm. Root has no MFA grace and no IAM policy limiting its blast radius. Treat any root failure as an incident.
How to fix it
You need a CloudTrail trail feeding CloudWatch Logs, then a metric filter and an alarm on top of it. The steps below assume your trail already ships to a log group. If it does not, set that up first.
Step 1: Create an SNS topic and subscribe to it
aws sns create-topic --name lensix-security-alerts
aws sns subscribe \
--topic-arn arn:aws:sns:us-east-1:111122223333:lensix-security-alerts \
--protocol email \
--notification-endpoint [email protected]
Confirm the subscription from the email AWS sends. An alarm pointed at a topic with no confirmed subscriber is silent, and the check will still treat your monitoring as incomplete in spirit.
Step 2: Create the metric filter
Point the filter at the CloudWatch Logs group your trail writes to. The pattern matches console logins where the result is a failure.
aws logs put-metric-filter \
--log-group-name CloudTrail/DefaultLogGroup \
--filter-name ConsoleSignInFailures \
--filter-pattern '{ ($.eventName = "ConsoleLogin") && ($.errorMessage = "Failed authentication") }' \
--metric-transformations \
metricName=ConsoleSignInFailureCount,metricNamespace=LensixSecurityMetrics,metricValue=1
Step 3: Create the CloudWatch alarm
aws cloudwatch put-metric-alarm \
--alarm-name ConsoleSignInFailureAlarm \
--alarm-description "Alerts on AWS console authentication failures (CIS 3.2)" \
--metric-name ConsoleSignInFailureCount \
--namespace LensixSecurityMetrics \
--statistic Sum \
--period 300 \
--threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold \
--evaluation-periods 1 \
--treat-missing-data notBreaching \
--alarm-actions arn:aws:sns:us-east-1:111122223333:lensix-security-alerts
A threshold of 1 over a five minute window is aggressive and will catch typos as well as attacks. If that creates too much noise, raise the threshold to 3 or widen the period. Tune it to your team, but err toward sensitivity for anything touching authentication.
Tip: Reuse a single SNS topic for all your CIS monitoring alarms (unauthorized API calls, IAM policy changes, root usage, and so on). One subscription, one place to manage who gets paged, and far less SNS sprawl.
Terraform version
If you manage infrastructure as code, define the whole chain so it cannot drift:
resource "aws_sns_topic" "security_alerts" {
name = "lensix-security-alerts"
}
resource "aws_cloudwatch_log_metric_filter" "console_signin_failures" {
name = "ConsoleSignInFailures"
log_group_name = aws_cloudwatch_log_group.cloudtrail.name
pattern = "{ ($.eventName = \"ConsoleLogin\") && ($.errorMessage = \"Failed authentication\") }"
metric_transformation {
name = "ConsoleSignInFailureCount"
namespace = "LensixSecurityMetrics"
value = "1"
}
}
resource "aws_cloudwatch_metric_alarm" "console_signin_failures" {
alarm_name = "ConsoleSignInFailureAlarm"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 1
metric_name = "ConsoleSignInFailureCount"
namespace = "LensixSecurityMetrics"
period = 300
statistic = "Sum"
threshold = 1
treat_missing_data = "notBreaching"
alarm_actions = [aws_sns_topic.security_alerts.arn]
alarm_description = "Alerts on AWS console authentication failures (CIS 3.2)"
}
Note: CIS alarms must be created in the same region as the CloudWatch Logs group that receives your trail. For a multi-region trail, that is the home region of the trail. You do not need the alarm in every region, just where the logs land.
How to prevent it from happening again
Manual alarms get deleted, drift, or never get created in new accounts. Bake the monitoring into the way you provision accounts.
- Ship it in your landing zone. If you use AWS Control Tower or a custom account factory, include the CIS alarm set in the baseline that every new account inherits. New accounts arrive already compliant.
- Use a reusable module. Wrap the metric filter, alarm, and SNS topic in a single Terraform or CloudFormation module and require it in every account stack. One module, many accounts.
- Gate it in CI/CD. Add a policy-as-code check so a pull request that removes or weakens the alarm fails before merge.
A simple Open Policy Agent rule that runs against your Terraform plan can assert the alarm exists:
package cis.monitoring
deny[msg] {
not has_console_failure_alarm
msg := "CIS 3.2: a console sign-in failure CloudWatch alarm is required"
}
has_console_failure_alarm {
some r
input.resource_changes[r].type == "aws_cloudwatch_metric_alarm"
contains(input.resource_changes[r].change.after.metric_name, "ConsoleSignInFailure")
}
Tip: Lensix continuously re-runs this check across every connected account, so even if an alarm is deleted out-of-band, you get told within the next scan rather than at your next annual audit.
Best practices
The alarm is necessary but it is not the whole story. Pair it with the controls that actually reduce the number of failures worth worrying about.
- Enforce MFA everywhere. A failed password matters far less when MFA stands between the attacker and the account. Require it on every IAM user and on root.
- Lock down root. Set a long, unique root password, enable hardware MFA, remove root access keys, and alarm separately on any root login, successful or not.
- Prefer federation over IAM users. Use IAM Identity Center or an external IdP so human access flows through SSO with conditional access, not standalone console passwords.
- Build the full CIS alarm set. CIS 3.x covers more than just sign-in failures: unauthorized API calls, IAM changes, S3 policy changes, network ACL changes, and route table changes all deserve alarms. Provision them together.
- Route alarms somewhere people read. Email works, but a topic that also fans out to Slack or PagerDuty gets faster eyes during an active attack.
Danger: Do not respond to a flurry of failed logins by deleting the targeted IAM user without checking for active sessions or attached automation. Disable the login profile and rotate credentials first, confirm nothing legitimate depends on that identity, then remove it. Deleting in a panic can break production pipelines.
Getting this check to pass takes ten minutes. Keeping it passing across a growing fleet of accounts is the real work, and that is where a baseline module plus continuous monitoring earns its keep.

