Skip to main content

CloudWatch Alarm for VPC Changes

Overview

This check verifies that your AWS account has a CloudWatch Logs metric filter and alarm configured to detect changes to Virtual Private Clouds (VPCs). It monitors CloudTrail events for VPC modifications including creating, deleting, or modifying VPCs and VPC peering connections.

When someone creates, deletes, or changes a VPC, you want to know about it. This alarm gives you that visibility.

Risk

Without monitoring VPC changes, unauthorized or accidental network modifications can go undetected. This creates serious security gaps:

  • Data exposure: A VPC peering connection to an untrusted account could allow data exfiltration
  • Lateral movement: Attackers who gain access might modify VPC attributes to expand their reach
  • Service disruption: Accidental VPC deletions or misconfigurations can cause outages
  • Compliance violations: Many frameworks require monitoring of network infrastructure changes

VPCs form the foundation of your network security. Changes to them deserve immediate attention.

Remediation Steps

Prerequisites

You need:

  • AWS Console access with permissions to create CloudWatch metric filters and alarms
  • An existing CloudTrail trail that sends logs to CloudWatch Logs
Required IAM permissions (for administrators)

Your IAM user or role needs these permissions:

  • logs:PutMetricFilter
  • logs:DescribeMetricFilters
  • logs:DescribeLogGroups
  • cloudwatch:PutMetricAlarm
  • cloudwatch:DescribeAlarms
  • sns:CreateTopic (if creating a new notification topic)
  • sns:Subscribe (if subscribing to notifications)
Setting up CloudTrail to CloudWatch Logs (if not already configured)

If your CloudTrail trail is not yet sending logs to CloudWatch Logs, you need to set this up first. See the cloudtrail_cloudwatch_logging_enabled remediation guide for detailed instructions.

AWS Console Method

Step 1: Create an SNS Topic for Notifications

  1. Go to Amazon SNS Console in us-east-1
  2. Click Topics in the left sidebar
  3. Click Create topic
  4. Select Standard type
  5. Enter a name like vpc-changes-alarm
  6. Click Create topic
  7. Click Create subscription
  8. Choose Email as the protocol
  9. Enter your email address
  10. Click Create subscription
  11. Check your email and confirm the subscription

Step 2: Create a Metric Filter

  1. Go to CloudWatch Console in us-east-1
  2. Click Log groups under Logs in the left sidebar
  3. Find and click on your CloudTrail log group (often named /aws/cloudtrail/<trail-name>)
  4. Click the Metric filters tab
  5. Click Create metric filter
  6. In the Filter pattern field, paste this pattern:
{ ($.eventName = CreateVpc) || ($.eventName = DeleteVpc) || ($.eventName = ModifyVpcAttribute) || ($.eventName = AcceptVpcPeeringConnection) || ($.eventName = CreateVpcPeeringConnection) || ($.eventName = DeleteVpcPeeringConnection) || ($.eventName = RejectVpcPeeringConnection) || ($.eventName = AttachClassicLinkVpc) || ($.eventName = DetachClassicLinkVpc) || ($.eventName = DisableVpcClassicLink) || ($.eventName = EnableVpcClassicLink) }
  1. Click Next
  2. Enter filter name: VPCChangesMetricFilter
  3. Enter metric namespace: CISBenchmark
  4. Enter metric name: vpc_changes_metric
  5. Enter metric value: 1
  6. Leave default value as 0
  7. Click Next, then Create metric filter

Step 3: Create an Alarm

  1. Still in CloudWatch, click All alarms under Alarms in the left sidebar
  2. Click Create alarm
  3. Click Select metric
  4. Navigate to CISBenchmark > Metrics with no dimensions
  5. Select vpc_changes_metric
  6. Click Select metric
  7. Configure the alarm:
    • Statistic: Sum
    • Period: 5 minutes
    • Threshold type: Static
    • Whenever vpc_changes_metric is: Greater/Equal than 1
  8. Click Next
  9. For notification:
    • Select In alarm
    • Choose Select an existing SNS topic
    • Select your vpc-changes-alarm topic
  10. Click Next
  11. Enter alarm name: VPCChangesAlarm
  12. Enter description: Alarm triggered when VPC configuration changes are detected
  13. Click Next, then Create alarm
AWS CLI (optional)

Step 1: Create an SNS Topic

aws sns create-topic \
--name vpc-changes-alarm \
--region us-east-1

Note the TopicArn in the output (format: arn:aws:sns:us-east-1:<account-id>:vpc-changes-alarm).

Subscribe your email:

aws sns subscribe \
--topic-arn arn:aws:sns:us-east-1:<your-account-id>:vpc-changes-alarm \
--protocol email \
--notification-endpoint your-email@example.com \
--region us-east-1

Step 2: Create the Metric Filter

Replace <your-cloudtrail-log-group> with your CloudTrail log group name:

aws logs put-metric-filter \
--log-group-name <your-cloudtrail-log-group> \
--filter-name VPCChangesMetricFilter \
--filter-pattern '{ ($.eventName = CreateVpc) || ($.eventName = DeleteVpc) || ($.eventName = ModifyVpcAttribute) || ($.eventName = AcceptVpcPeeringConnection) || ($.eventName = CreateVpcPeeringConnection) || ($.eventName = DeleteVpcPeeringConnection) || ($.eventName = RejectVpcPeeringConnection) || ($.eventName = AttachClassicLinkVpc) || ($.eventName = DetachClassicLinkVpc) || ($.eventName = DisableVpcClassicLink) || ($.eventName = EnableVpcClassicLink) }' \
--metric-transformations metricName=vpc_changes_metric,metricNamespace=CISBenchmark,metricValue=1,defaultValue=0 \
--region us-east-1

Step 3: Create the CloudWatch Alarm

aws cloudwatch put-metric-alarm \
--alarm-name VPCChangesAlarm \
--alarm-description "Alarm triggered when VPC configuration changes are detected" \
--metric-name vpc_changes_metric \
--namespace CISBenchmark \
--statistic Sum \
--period 300 \
--evaluation-periods 1 \
--threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold \
--alarm-actions arn:aws:sns:us-east-1:<your-account-id>:vpc-changes-alarm \
--treat-missing-data notBreaching \
--region us-east-1
CloudFormation (optional)

This template creates the complete monitoring setup including SNS topic, metric filter, and alarm:

AWSTemplateFormatVersion: '2010-09-09'
Description: CloudWatch alarm for VPC changes (CIS Benchmark)

Parameters:
CloudTrailLogGroupName:
Type: String
Description: Name of the CloudTrail CloudWatch Logs log group
Default: /aws/cloudtrail/my-trail

NotificationEmail:
Type: String
Description: Email address for alarm notifications
AllowedPattern: ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
ConstraintDescription: Must be a valid email address

Resources:
VPCChangesSnsTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: vpc-changes-alarm
DisplayName: VPC Changes Alarm

VPCChangesEmailSubscription:
Type: AWS::SNS::Subscription
Properties:
TopicArn: !Ref VPCChangesSnsTopic
Protocol: email
Endpoint: !Ref NotificationEmail

VPCChangesMetricFilter:
Type: AWS::Logs::MetricFilter
Properties:
LogGroupName: !Ref CloudTrailLogGroupName
FilterName: VPCChangesMetricFilter
FilterPattern: >-
{ ($.eventName = CreateVpc) || ($.eventName = DeleteVpc) ||
($.eventName = ModifyVpcAttribute) || ($.eventName = AcceptVpcPeeringConnection) ||
($.eventName = CreateVpcPeeringConnection) || ($.eventName = DeleteVpcPeeringConnection) ||
($.eventName = RejectVpcPeeringConnection) || ($.eventName = AttachClassicLinkVpc) ||
($.eventName = DetachClassicLinkVpc) || ($.eventName = DisableVpcClassicLink) ||
($.eventName = EnableVpcClassicLink) }
MetricTransformations:
- MetricName: vpc_changes_metric
MetricNamespace: CISBenchmark
MetricValue: '1'
DefaultValue: 0

VPCChangesAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: VPCChangesAlarm
AlarmDescription: Alarm triggered when VPC configuration changes are detected
MetricName: vpc_changes_metric
Namespace: CISBenchmark
Statistic: Sum
Period: 300
EvaluationPeriods: 1
Threshold: 1
ComparisonOperator: GreaterThanOrEqualToThreshold
TreatMissingData: notBreaching
AlarmActions:
- !Ref VPCChangesSnsTopic

Outputs:
AlarmArn:
Description: ARN of the VPC changes alarm
Value: !GetAtt VPCChangesAlarm.Arn

SnsTopicArn:
Description: ARN of the SNS topic for notifications
Value: !Ref VPCChangesSnsTopic

Deploy with:

aws cloudformation deploy \
--template-file vpc-changes-alarm.yaml \
--stack-name vpc-changes-monitoring \
--parameter-overrides \
CloudTrailLogGroupName=/aws/cloudtrail/my-trail \
NotificationEmail=your-email@example.com \
--region us-east-1
Terraform (optional)
# Variables
variable "cloudtrail_log_group_name" {
description = "Name of the CloudTrail CloudWatch Logs log group"
type = string
default = "/aws/cloudtrail/my-trail"
}

variable "notification_email" {
description = "Email address for alarm notifications"
type = string
}

# SNS Topic for notifications
resource "aws_sns_topic" "vpc_changes" {
name = "vpc-changes-alarm"
display_name = "VPC Changes Alarm"
}

resource "aws_sns_topic_subscription" "vpc_changes_email" {
topic_arn = aws_sns_topic.vpc_changes.arn
protocol = "email"
endpoint = var.notification_email
}

# Metric filter for VPC changes
resource "aws_cloudwatch_log_metric_filter" "vpc_changes" {
name = "VPCChangesMetricFilter"
pattern = "{ ($.eventName = CreateVpc) || ($.eventName = DeleteVpc) || ($.eventName = ModifyVpcAttribute) || ($.eventName = AcceptVpcPeeringConnection) || ($.eventName = CreateVpcPeeringConnection) || ($.eventName = DeleteVpcPeeringConnection) || ($.eventName = RejectVpcPeeringConnection) || ($.eventName = AttachClassicLinkVpc) || ($.eventName = DetachClassicLinkVpc) || ($.eventName = DisableVpcClassicLink) || ($.eventName = EnableVpcClassicLink) }"
log_group_name = var.cloudtrail_log_group_name

metric_transformation {
name = "vpc_changes_metric"
namespace = "CISBenchmark"
value = "1"
default_value = "0"
}
}

# CloudWatch alarm
resource "aws_cloudwatch_metric_alarm" "vpc_changes" {
alarm_name = "VPCChangesAlarm"
alarm_description = "Alarm triggered when VPC configuration changes are detected"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 1
metric_name = "vpc_changes_metric"
namespace = "CISBenchmark"
period = 300
statistic = "Sum"
threshold = 1
treat_missing_data = "notBreaching"

alarm_actions = [aws_sns_topic.vpc_changes.arn]

depends_on = [aws_cloudwatch_log_metric_filter.vpc_changes]
}

# Outputs
output "alarm_arn" {
description = "ARN of the VPC changes alarm"
value = aws_cloudwatch_metric_alarm.vpc_changes.arn
}

output "sns_topic_arn" {
description = "ARN of the SNS topic for notifications"
value = aws_sns_topic.vpc_changes.arn
}

Deploy with:

terraform init
terraform plan -var="notification_email=your-email@example.com"
terraform apply -var="notification_email=your-email@example.com"

Verification

After setting up the alarm, verify everything is working:

  1. Check the metric filter exists:

    • Go to CloudWatch > Log groups > your CloudTrail log group
    • Click the Metric filters tab
    • Verify VPCChangesMetricFilter appears in the list
  2. Check the alarm is configured:

    • Go to CloudWatch > All alarms
    • Find VPCChangesAlarm
    • Verify status shows OK or Insufficient data (not In alarm unless there were recent VPC changes)
  3. Test the alarm (optional):

    • Create a test VPC in the console or CLI
    • Wait 5-10 minutes
    • Check that you receive an email notification
    • Delete the test VPC when done
CLI verification commands

Check if the metric filter exists:

aws logs describe-metric-filters \
--log-group-name <your-cloudtrail-log-group> \
--filter-name-prefix VPCChanges \
--region us-east-1

Check if the alarm exists and its state:

aws cloudwatch describe-alarms \
--alarm-names VPCChangesAlarm \
--region us-east-1 \
--query 'MetricAlarms[0].{Name:AlarmName,State:StateValue,Metric:MetricName}'

Expected output shows the alarm exists with StateValue of OK or INSUFFICIENT_DATA.

Additional Resources

Notes

  • CloudTrail requirement: This alarm requires CloudTrail to be configured to send logs to CloudWatch Logs. If you see "Insufficient data" for extended periods, verify your CloudTrail integration is working.

  • False positives: Legitimate VPC changes (like creating new environments) will trigger this alarm. Consider this expected behavior - the goal is visibility, not necessarily blocking changes.

  • Multiple alarms: For comprehensive CIS Benchmark compliance, you may need similar alarms for other resource types (security groups, network ACLs, route tables, etc.).

  • Costs: CloudWatch alarms and metric filters have minimal cost, but consider the volume of CloudTrail logs and any additional log storage costs.

  • Classic Link events: The filter includes VPC Classic Link events. If you don't use EC2-Classic (most accounts don't), these events won't occur, but including them ensures complete coverage.

  • Response procedures: Consider documenting what actions to take when this alarm fires, such as identifying who made the change and whether it was authorized.