Skip to main content

Route Table Changes Alarm Should Be Enabled

More Info:​

AWS Route Tables 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)
  • 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
  • NIST CSF
  • 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 step‑by‑step instructions (AWS Console only) to create a CloudWatch alarm for route table changes using CloudTrail logs.


Prerequisites​

  1. CloudTrail must be enabled and logging management events (at least Write events) for EC2.
  2. CloudTrail must be delivering logs to a CloudWatch Logs log group.
    • If not configured, in the CloudTrail console → Trails → your trail → Edit → CloudWatch Logs and attach/create a log group and an IAM role.

Step 1: Identify/Create the CloudTrail Log Group​

  1. Open CloudTrail console.
  2. In the left menu, select Trails.
  3. Click your active trail.
  4. Scroll to CloudWatch Logs section:
    • Note the CloudWatch Logs log group name.
    • If none is configured, enable it:
      • Click Edit.
      • Under CloudWatch Logs, choose or create a log group (e.g., /aws/cloudtrail/management).
      • Choose/create an IAM role as prompted.
      • Save.

Step 2: Create a Metric Filter for Route Table Changes​

  1. Open CloudWatch console.

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

  3. Click the CloudTrail log group you identified (e.g., /aws/cloudtrail/management).

  4. Go to the Metric filters tab.

  5. Click Create metric filter.

  6. In Filter pattern, use a pattern that matches route-table–related API calls, for example:

    { ($.eventSource = "ec2.amazonaws.com") &&
    (
    ($.eventName = "CreateRoute") ||
    ($.eventName = "CreateRouteTable") ||
    ($.eventName = "ReplaceRoute") ||
    ($.eventName = "ReplaceRouteTableAssociation") ||
    ($.eventName = "DeleteRoute") ||
    ($.eventName = "DeleteRouteTable") ||
    ($.eventName = "DisassociateRouteTable")
    )
    }
  7. Click Next.

  8. For Filter name, enter something like:
    RouteTableChangesFilter

  9. Under Metric details:

    • Metric namespace: e.g., Security/NetworkChanges
    • Metric name: e.g., RouteTableChanges
    • Metric value: 1
    • Default value: leave blank or 0.
  10. Click Next, then Create metric filter.


Step 3: Create a CloudWatch Alarm on That Metric​

  1. After creating the filter, you’ll see it listed.
    Select the filter and click Create alarm (or go to CloudWatch → Alarms → All alarms → Create alarm and select the metric you just created:
    Security/NetworkChanges → RouteTableChanges).
  2. In the Specify metric and conditions step:
    • Statistic: Sum
    • Period: e.g., 5 minutes
    • Threshold type: Static
    • Condition:
      • Greater than
      • Threshold value: 0
    • This means: alarm if at least one route table change occurs in 5 minutes.
  3. Click Next.

Step 4: Configure Alarm Notifications​

  1. In Configure actions:
    • Alarm state trigger: In alarm
    • Under Notification, choose an SNS topic to send alerts to:
      • Select an existing topic (e.g., security-alerts), or
      • Click Create new topic, provide:
        • Topic name: e.g., route-table-changes-alerts
        • Email endpoint(s) (security, ops, etc.).
    • Confirm email subscription(s) if prompted.
  2. (Optional) Add additional actions (e.g., OpsCenter, EC2 action – normally not needed here).
  3. Click Next.

Step 5: Name and Create the Alarm​

  1. Provide:
    • Alarm name: RouteTableChangesAlarm
    • Alarm description: e.g., Alerts on any changes to VPC route tables using CloudTrail events.
  2. Review all settings.
  3. Click Create alarm.

  1. Make a test route table change (e.g., add a non-impactful route in a test VPC and remove it).
  2. Wait for the metric to update (typically a few minutes).
  3. Confirm:
    • Alarm transitions to ALARM state.
    • Notification (email/SNS) is received.

This completes enabling a CloudWatch alarm for route table changes using the AWS Console.

Using CLI

Below is a minimal, end‑to‑end way to enable an alarm for Route Table changes in AWS using the AWS CLI.

Assumptions:

  • You already have a CloudTrail trail sending events to a CloudWatch Logs log group (if not, steps 1–2 cover it).
  • Replace all placeholder values (e.g., MY_TRAIL, my-log-group, my-route-table-changes-metric, my-route-table-changes-alarm, etc.) with your own.

1. (If needed) Configure CloudTrail to send logs to CloudWatch Logs​

# 1.1 Create a log group for CloudTrail if you don’t have one
aws logs create-log-group --log-group-name /aws/cloudtrail/management

# 1.2 Attach a role/policy for CloudTrail to write to CloudWatch Logs
# Create an IAM role and policy separately (if not already present).
# Example role name: CloudTrail_CloudWatchLogs_Role

# 1.3 Update / create the trail to send logs to CloudWatch Logs
aws cloudtrail update-trail \
--name MY_TRAIL \
--cloud-watch-logs-log-group-arn arn:aws:logs:REGION:ACCOUNT_ID:log-group:/aws/cloudtrail/management \
--cloud-watch-logs-role-arn arn:aws:iam::ACCOUNT_ID:role/CloudTrail_CloudWatchLogs_Role

2. Create a CloudWatch Logs Metric Filter for Route Table changes​

The filter will match API calls that modify Route Tables:

  • CreateRouteTable
  • DeleteRouteTable
  • AssociateRouteTable
  • DisassociateRouteTable
  • ReplaceRouteTableAssociation
  • ReplaceRouteTableRoute
  • CreateRoute
  • DeleteRoute
  • ReplaceRoute
aws logs put-metric-filter \
--log-group-name /aws/cloudtrail/management \
--filter-name route-table-changes-filter \
--filter-pattern '{ ($.eventName = "CreateRouteTable") || ($.eventName = "DeleteRouteTable") || ($.eventName = "AssociateRouteTable") || ($.eventName = "DisassociateRouteTable") || ($.eventName = "ReplaceRouteTableAssociation") || ($.eventName = "ReplaceRouteTableRoute") || ($.eventName = "CreateRoute") || ($.eventName = "DeleteRoute") || ($.eventName = "ReplaceRoute") }' \
--metric-transformations \
metricName=my-route-table-changes-metric,metricNamespace=Security,metricValue=1

This will emit a metric Security/my-route-table-changes-metric with value 1 for every matched event.


3. Create an SNS topic for notifications (if you don’t have one)​

aws sns create-topic --name route-table-changes-topic

# Capture the TopicArn from the output, e.g.:
# arn:aws:sns:REGION:ACCOUNT_ID:route-table-changes-topic

# Optionally subscribe an email
aws sns subscribe \
--topic-arn arn:aws:sns:REGION:ACCOUNT_ID:route-table-changes-topic \
--protocol email \
--notification-endpoint you@example.com

4. Create a CloudWatch Alarm on the metric​

Alarm if at least 1 route table change occurs in a 5-minute period:

aws cloudwatch put-metric-alarm \
--alarm-name my-route-table-changes-alarm \
--alarm-description "Alarm when VPC Route Tables are changed" \
--metric-name my-route-table-changes-metric \
--namespace Security \
--statistic Sum \
--period 300 \
--threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold \
--evaluation-periods 1 \
--alarm-actions arn:aws:sns:REGION:ACCOUNT_ID:route-table-changes-topic \
--treat-missing-data notBreaching

Once done:

  • Any change to Route Tables (via the listed API calls) will be logged by CloudTrail into the log group.
  • The metric filter will emit a metric per event.
  • The CloudWatch alarm will trigger and send a notification via SNS whenever at least one change is detected in the evaluation period.
Using Python

Below is one straightforward way to remediate this in AWS using Python/boto3:

Goal:

  • Detect route table changes via CloudTrail
  • Send them to CloudWatch Logs
  • Use a metric filter and CloudWatch Alarm to trigger notifications (e.g., via SNS)

Assumptions:

  • You have AWS credentials configured.
  • You have boto3 installed.
  • You have (or will create) an SNS topic and subscription for notifications.

1. Enable/Verify CloudTrail With CloudWatch Logs​

CloudTrail must log management events and send them to a CloudWatch Logs log group.

import boto3

region = "us-east-1"
trail_name = "org-route-table-changes-trail"
log_group_name = "/aws/cloudtrail/route-table-changes"

logs = boto3.client("logs", region_name=region)
ct = boto3.client("cloudtrail", region_name=region)

# 1a. Create log group if it doesn't exist
def ensure_log_group():
try:
logs.create_log_group(logGroupName=log_group_name)
except logs.exceptions.ResourceAlreadyExistsException:
pass

# 1b. Create or update a trail that sends logs to CloudWatch Logs
def ensure_trail():
# Replace with an existing S3 bucket used for CloudTrail
s3_bucket_name = "my-cloudtrail-logs-bucket"

# Create or update trail
trails = ct.describe_trails(trailNameList=[trail_name])["trailList"]
if not trails:
ct.create_trail(
Name=trail_name,
S3BucketName=s3_bucket_name,
IsMultiRegionTrail=True,
IncludeGlobalServiceEvents=True,
IsOrganizationTrail=False,
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{region}:YOUR_ACCOUNT_ID:log-group:{log_group_name}",
CloudWatchLogsRoleArn="arn:aws:iam::YOUR_ACCOUNT_ID:role/CloudTrail_CloudWatchLogs_Role",
)
else:
ct.update_trail(
Name=trail_name,
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{region}:YOUR_ACCOUNT_ID:log-group:{log_group_name}",
CloudWatchLogsRoleArn="arn:aws:iam::YOUR_ACCOUNT_ID:role/CloudTrail_CloudWatchLogs_Role",
)

# Ensure logging is on
ct.start_logging(Name=trail_name)


ensure_log_group()
ensure_trail()

Notes:

  • You must have an IAM role (CloudTrail_CloudWatchLogs_Role) with the proper trust and permissions for CloudTrail → CloudWatch Logs.
  • Replace YOUR_ACCOUNT_ID, bucket name, role ARN as needed.

2. Create a Metric Filter for Route Table Changes​

Filter CloudTrail events for route-table–related eventNames on EC2.

Common relevant events:

  • CreateRoute, DeleteRoute, ReplaceRoute
  • CreateRouteTable, DeleteRouteTable
  • AssociateRouteTable, DisassociateRouteTable, ReplaceRouteTableAssociation
metric_filter_name = "RouteTableChangesFilter"
metric_namespace = "SecurityMonitoring"
metric_name = "RouteTableChangeCount"

filter_pattern = """
{ ($.eventSource = "ec2.amazonaws.com") &&
(
$.eventName = "CreateRoute" ||
$.eventName = "DeleteRoute" ||
$.eventName = "ReplaceRoute" ||
$.eventName = "CreateRouteTable" ||
$.eventName = "DeleteRouteTable" ||
$.eventName = "AssociateRouteTable" ||
$.eventName = "DisassociateRouteTable" ||
$.eventName = "ReplaceRouteTableAssociation"
)
}
"""

logs.put_metric_filter(
logGroupName=log_group_name,
filterName=metric_filter_name,
filterPattern=filter_pattern.strip(),
metricTransformations=[
{
"metricName": metric_name,
"metricNamespace": metric_namespace,
"metricValue": "1",
}
],
)

3. Create an SNS Topic (If Needed)​

sns = boto3.client("sns", region_name=region)
topic_name = "route-table-changes-topic"

topic_arn = sns.create_topic(Name=topic_name)["TopicArn"]

# Optionally subscribe an email
sns.subscribe(
TopicArn=topic_arn,
Protocol="email",
Endpoint="security-team@example.com",
)
print("Confirm the email subscription from your inbox.")

4. Create a CloudWatch Alarm on the Metric​

Alarm when at least 1 route table change occurs in a 5-minute period.

cw = boto3.client("cloudwatch", region_name=region)

alarm_name = "RouteTableChangesAlarm"

cw.put_metric_alarm(
AlarmName=alarm_name,
AlarmDescription="Alarm when any VPC route table is modified.",
Namespace=metric_namespace,
MetricName=metric_name,
Statistic="Sum",
Period=300, # 5 minutes
EvaluationPeriods=1,
Threshold=1.0,
ComparisonOperator="GreaterThanOrEqualToThreshold",
TreatMissingData="notBreaching",
AlarmActions=[topic_arn],
)

5. Validate​

  • Make a test change to a route table.
  • Wait a few minutes.
  • Verify:
    • CloudTrail shows the event.
    • Metric in CloudWatch (SecurityMonitoring/RouteTableChangeCount) increments.
    • Alarm transitions to ALARM and sends an SNS notification.

This configuration ensures “Route Table Changes Alarm” is enabled via CloudWatch and fully managed by Python/boto3.

Using Terraform
# CloudWatch Logs metric filter for route table changes
resource "aws_cloudwatch_log_metric_filter" "route_table_changes" {
name = "RouteTableChangesFilter"
log_group_name = "CLOUDTRAIL_LOG_GROUP_NAME" # replace with your CloudTrail log group name

pattern = "{ ($.eventName = CreateRoute) || ($.eventName = CreateRouteTable) || ($.eventName = ReplaceRoute) || ($.eventName = ReplaceRouteTableAssociation) || ($.eventName = DeleteRouteTable) || ($.eventName = DeleteRoute) || ($.eventName = DisassociateRouteTable) }"

metric_transformation {
name = "RouteTableChangesMetric"
namespace = "CloudTrailMetrics"
value = "1"
}
}

# Optional: SNS topic for alarm notifications (or use an existing one)
resource "aws_sns_topic" "route_table_changes_notifications" {
name = "RouteTableChangeNotifications"
}

# CloudWatch alarm for the route table changes metric
resource "aws_cloudwatch_metric_alarm" "route_table_changes" {
alarm_name = "RouteTableChangesAlarm"
alarm_description = "Monitors for route table changes"
namespace = "CloudTrailMetrics"
metric_name = "RouteTableChangesMetric"
statistic = "Sum"
period = 300
evaluation_periods = 1
threshold = 1
comparison_operator = "GreaterThanOrEqualToThreshold"

alarm_actions = [
aws_sns_topic.route_table_changes_notifications.arn
# or replace with an existing SNS topic ARN:
# "ROUTE_TABLE_CHANGES_ALARM_SNS_TOPIC_ARN"
]

depends_on = [aws_cloudwatch_log_metric_filter.route_table_changes]
}

This change will create (or, if the same names already exist and are imported, update) the metric filter RouteTableChangesFilter and alarm RouteTableChangesAlarm; if they are currently unmanaged by Terraform, importing or replacing them may overwrite existing settings. After adding this, terraform plan should show one aws_cloudwatch_log_metric_filter and one aws_cloudwatch_metric_alarm (and optionally aws_sns_topic) to be created or updated with the exact pattern, metric, and threshold described above.

Additional Reading:​