In production security, you must maintain an audit log of every API call made in your AWS account. If a resource is deleted, an IAM user is modified, or a security group rule is altered, you must be able to trace who did it, from where, and when. AWS CloudTrail captures these actions, but to act on them in real-time, you must stream those logs to CloudWatch Logs.
In this guide, we'll design a real-time security audit pipeline. We will stream CloudTrail logs to CloudWatch, define metric filters for critical security events, and deploy the entire setup using Terraform.
Incident Prevention: Setting up CloudWatch alarms for unauthorized actions or root logins ensures security teams are notified within seconds of a critical policy breach.
The Architecture
- CloudTrail: Captures write/management API actions across all AWS regions.
- S3 Bucket: Serves as the immutable, long-term historical audit log repository.
- CloudWatch Logs: CloudTrail assumes an IAM role to write logs to a log group, enabling real-time stream analysis and alerting.
Terraform Code: The Audit Pipeline
Let's write the Terraform configuration to provision this secure pipeline. We'll set up the CloudWatch Log Group, S3 storage, IAM log writer roles, and configure the Trail itself.
# 1. CloudWatch Log Group for Trails resource "aws_cloudwatch_log_group" "trail" { name = "/aws/cloudtrail/security-audit" retention_in_days = 90 } # 2. S3 Bucket for Long-term Storage resource "aws_s3_bucket" "trail" { bucket = "company-audit-logs-222222222222" force_destroy = true } # 3. IAM Role for CloudTrail logging to CloudWatch resource "aws_iam_role" "trail" { name = "CloudTrailToCloudWatchLoggingRole" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Effect = "Allow" Principal = { Service = "cloudtrail.amazonaws.com" } Action = "sts:AssumeRole" }] }) } # 4. IAM Policy allowing Log Delivery resource "aws_iam_role_policy" "trail" { name = "CloudTrailToCloudWatchPolicy" role = aws_iam_role.trail.id policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = ["logs:CreateLogStream", "logs:PutLogEvents"] Resource = "${aws_cloudwatch_log_group.trail.arn}:*" } ] }) } # 5. CloudTrail configuration resource "aws_cloudtrail" "main" { name = "organizational-audit-trail" s3_bucket_name = aws_s3_bucket.trail.id include_global_service_events = true is_multi_region_trail = true enable_log_file_validation = true cloud_watch_logs_group_arn = "${aws_cloudwatch_log_group.trail.arn}:*" cloud_watch_logs_role_arn = aws_iam_role.trail.arn }
Step 2: Define Metric Filters for Alerting
With logs streaming to CloudWatch, we configure **Metric Filters** to parse audit records and increment metrics when critical conditions match.
Filter 1: Alert on Unauthorized Operations
Trigger an alert if an API call fails due to permissions (e.g., someone trying to probe resources without access):
- Filter Pattern:
{ ($.errorCode = "*UnauthorizedOperation") || ($.errorCode = "AccessDenied") }
Filter 2: Alert on Security Group Modifications
Trigger an alert if security groups are modified (creating public holes in firewalls):
- Filter Pattern:
{ ($.eventName = AuthorizeSecurityGroupIngress) || ($.eventName = RevokeSecurityGroupIngress) || ($.eventName = CreateSecurityGroup) }
Here is how we configure the Unauthorized Action filter in Terraform:
resource "aws_cloudwatch_log_metric_filter" "unauthorized_api" { name = "UnauthorizedAPICallsCount" pattern = "{ ($.errorCode = \"*UnauthorizedOperation\") || ($.errorCode = \"AccessDenied\") }" log_group_name = aws_cloudwatch_log_group.trail.name metric_transformation { name = "AccessDeniedEvents" namespace = "CloudTrailSecurityAudit" value = "1" } }
Step 3: Alertmanager / SNS Notifications
Finally, create a CloudWatch Alarm targeting your metric. If the count of AccessDeniedEvents exceeds 5 within a 5-minute window, the alarm transitions to the ALARM state, routing notifications to an SNS Topic connected to PagerDuty, Slack, or Email channels.
Conclusion
Streaming CloudTrail logs to CloudWatch Logs provides full audit visibility. By codifying this architecture in Terraform and establishing alerting metrics for unauthorized calls, you secure your cloud assets and gain immediate visibility into potential incidents.