Skip to content

RDS DB instances should be integrated with CloudWatch logs

Database logs left on the instance are ephemeral. RDS storage can rotate or lose logs during failovers, restarts, or storage pressure events. Streaming logs to CloudWatch Logs makes them durable, searchable, and available for metric filters and alarms without SSH or RDS API polling.

CloudWatch integration also unlocks cross-service correlation. When an application error in Lambda or ECS coincides with a slow query or authentication failure in RDS, having both log streams in the same destination cuts mean-time-to-diagnosis significantly.

Retrofit consideration

Enabling log exports on existing instances is typically applied without downtime, though changes go through normal RDS modify/apply timing. For MariaDB, exporting the audit log requires enabling the MariaDB audit plugin settings (for example, server_audit_logging=1) in a custom parameter group, which may require a reboot depending on parameter apply type.

Implementation

Choose the approach that matches how you manage Terraform.

If you use terraform-aws-modules/rds/aws, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.

module "rds" {
  source  = "terraform-aws-modules/rds/aws"
  version = ">=7.0.0"

  allocated_storage      = 20
  db_name                = "myapp"
  db_subnet_group_name   = "example-db-subnet-group"
  engine                 = "mysql"
  engine_version         = "8.0.41"
  family                 = "mysql8.0"
  identifier             = "abc123"
  instance_class         = "db.t3.micro"
  major_engine_version   = "8.0"
  password_wo            = "change-me-in-production"
  skip_final_snapshot    = true
  username               = "dbadmin"
  vpc_security_group_ids = ["sg-12345678"]
}

Use AWS provider resources directly. See docs for the resources involved: aws_db_instance.

resource "aws_db_instance" "this" {
  allocated_storage               = 20
  enabled_cloudwatch_logs_exports = ["general", "slowquery"]
  engine                          = "mysql"
  identifier                      = "pofix-abc123"
  instance_class                  = "db.t3.micro"
  monitoring_interval             = 60
  monitoring_role_arn             = "arn:aws:iam::123456789012:role/example-role"
  password                        = "ChangeMe123!"
  skip_final_snapshot             = true
  username                        = "dbadmin"
}

What this control checks

In the aws_db_instance resource, enabled_cloudwatch_logs_exports must be a non-empty list of log type strings appropriate for the database engine. For MySQL and MariaDB, valid values include "audit", "error", "general", and "slowquery". For PostgreSQL, valid values are "postgresql" and "upgrade". For Oracle, use "alert", "audit", "listener", and "trace". For SQL Server, use "agent" and "error". A configuration that omits enabled_cloudwatch_logs_exports entirely or sets it to [] fails this control. RDS handles log delivery to CloudWatch Logs automatically once export is configured.

Common pitfalls

  • Engine-specific log types cause silent no-ops

    Specifying a log type that doesn't match the engine (for example, "slowquery" on a PostgreSQL instance) produces a Terraform apply error, which is at least visible. The subtler problem: specifying "audit" on MySQL without setting server_audit_logging = 1 in the DB parameter group creates an empty CloudWatch log group. The control passes, but nothing is actually being logged.

  • Aurora instances use a different resource

    This control targets aws_db_instance. Aurora clusters configure log exports on aws_rds_cluster using the same enabled_cloudwatch_logs_exports argument. If your fleet includes both Aurora and non-Aurora databases, you need to address both resource types separately.

  • CloudWatch Logs retention defaults to never expire

    Log groups under /aws/rds/instance/ default to indefinite retention. Storage costs grow unbounded unless you set a retention policy via aws_cloudwatch_log_group with retention_in_days. Terraform can import or pre-create these log groups to manage retention before RDS creates them.

  • Read replicas need independent configuration

    enabled_cloudwatch_logs_exports on a read replica aws_db_instance does not inherit from the source instance. Each replica must declare its own log exports explicitly.

Audit evidence

Auditors expect compliance evaluation results (from Config custom rules or conformance packs) showing all RDS DB instances as compliant. On the RDS instance Configuration tab, one or more log types should appear under "Published logs" pointing to CloudWatch Logs. The CloudWatch Logs console should contain log groups under the /aws/rds/instance/<instance-id>/<log-type> naming convention with recent log streams, confirming active delivery.

For deeper validation, CloudTrail ModifyDBInstance events should show EnableCloudwatchLogsExports in the request parameters, and Logs Insights queries against the exported log groups should return recent entries matching the expected engine log format.

Framework-specific interpretation

Tool mappings

Use these identifiers to cross-reference this control across tools, reports, and evidence.

  • Compliance.tf Control: rds_db_instance_cloudwatch_logs_enabled

  • AWS Config Managed Rules: RDS_LOGGING_ENABLED, RDS_POSTGRESQL_LOGS_TO_CLOUDWATCH, RDS_SQL_SERVER_LOGS_TO_CLOUDWATCH

  • Checkov Check: CKV_AWS_129

  • Powerpipe Control: aws_compliance.control.rds_db_instance_cloudwatch_logs_enabled

  • Prowler Check: rds_instance_integration_cloudwatch_logs

  • AWS Security Hub Controls: RDS.40, RDS.42, RDS.9

Last reviewed: 2026-03-09