Check ID: cloudwatch_log_metric_filter_aws_organizations_changes
Provider: AWS
Service: cloudwatch
Severity: MEDIUM
Categories: logging
Resource type: AwsCloudWatchAlarm

What this check verifies

CloudWatch Logs metric filters and alarms monitor AWS Organizations change events recorded by CloudTrail, including actions like CreateAccount, AttachPolicy, MoveAccount, and UpdateOrganizationalUnit. The evaluation looks for a filter on the trail log group matching organizations.amazonaws.com events and an alarm linked to that metric.

Risk

Without alerting on AWS Organizations changes, attackers or misconfigurations can silently alter governance, enabling unauthorized access and policy bypass. They could create/remove accounts, change or detach SCPs, or delete the organization, risking data exposure (C), privilege escalation (I), and service disruption (A).

Recommendation

Send CloudTrail events to CloudWatch Logs, add a metric filter for organizations.amazonaws.com change events, and attach an alarm that notifies responders. Enforce least privilege and separation of duties for org admins, require MFA and approvals, and regularly test alerts to ensure timely detection and response.

Remediation

# CloudWatch Logs metric filter for AWS Organizations changes
resource "aws_cloudwatch_log_metric_filter" "organizations_changes" {
  name           = "<example_resource_name>"
  log_group_name = "<example_log_group_name>"
  pattern        = "{ ($.eventSource = organizations.amazonaws.com) && (($.eventName = \"AcceptHandshake\") || ($.eventName = \"AttachPolicy\") || ($.eventName = \"CancelHandshake\") || ($.eventName = \"CreateAccount\") || ($.eventName = \"CreateOrganization\") || ($.eventName = \"CreateOrganizationalUnit\") || ($.eventName = \"CreatePolicy\") || ($.eventName = \"DeclineHandshake\") || ($.eventName = \"DeleteOrganization\") || ($.eventName = \"DeleteOrganizationalUnit\") || ($.eventName = \"DeletePolicy\") || ($.eventName = \"EnableAllFeatures\") || ($.eventName = \"EnablePolicyType\") || ($.eventName = \"InviteAccountToOrganization\") || ($.eventName = \"LeaveOrganization\") || ($.eventName = \"DetachPolicy\") || ($.eventName = \"DisablePolicyType\") || ($.eventName = \"MoveAccount\") || ($.eventName = \"RemoveAccountFromOrganization\") || ($.eventName = \"UpdateOrganizationalUnit\") || ($.eventName = \"UpdatePolicy\")) }"  # Critical: matches AWS Organizations change events

  metric_transformation {
    name      = "<example_resource_name>"   # Critical: metric created by the filter
    namespace = "CISBenchmark"              # Critical: used by the alarm
    value     = "1"
  }
}

# Alarm on the metric generated by the filter
resource "aws_cloudwatch_metric_alarm" "organizations_changes" {
  alarm_name          = "<example_resource_name>"
  comparison_operator = "GreaterThanOrEqualToThreshold"
  evaluation_periods  = 1
  metric_name         = "<example_resource_name>"  # Critical: matches metric filter name
  namespace           = "CISBenchmark"             # Critical: matches metric filter namespace
  period              = 300
  statistic           = "Sum"
  threshold           = 1
}

References

Where this check fires

This check runs on every scheduled scan against your AWS account. Findings appear at Cloud Security → Findings filterable by Check ID = cloudwatch_log_metric_filter_aws_organizations_changes. To re-evaluate after a fix, hit Run now on the account’s schedule under Cloud Security → Schedules — the next scan re-checks this control and marks the finding fixed or keeps it persistent.
← Back to AWS checks