Skip to content

RDS for SQL Server DB instances should publish logs to CloudWatch Logs

SQL Server database logs contain error messages, agent job history, and optionally SQL Server Audit records. Without CloudWatch Logs integration, these logs exist only on the RDS instance's local storage, where they rotate and eventually disappear. You lose the ability to set CloudWatch alarms on error patterns, run Logs Insights queries across your fleet, or retain logs beyond the instance's lifecycle.

Forwarding logs to CloudWatch decouples log retention from instance operations. If someone deletes or resizes an instance, the logs survive independently. That matters during incident investigations where you need weeks or months of history.

Retrofit consideration

Enabling CloudWatch Logs exports on an existing RDS SQL Server instance triggers a modification that may cause a brief log delivery delay but does not require a reboot. If the instance lacks the required IAM service-linked role permissions or the target CloudWatch log group does not exist, the modification can fail silently.

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 = ["agent", "error"]
  engine                          = "sqlserver-ex"
  identifier                      = "pofix-abc123"
  instance_class                  = "db.t3.small"
  license_model                   = "license-included"
  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

This control validates that the aws_db_instance resource for a SQL Server engine family has enabled_cloudwatch_logs_exports set with at least one valid log type. For SQL Server engines (sqlserver-ee, sqlserver-se, sqlserver-ex, sqlserver-web), the accepted log types are "error" and "agent". An instance passes when enabled_cloudwatch_logs_exports contains one or both values. It fails when the argument is omitted entirely or set to an empty list. The control may optionally require specific log types depending on the parameter configuration. No additional IAM or CloudWatch resources need to be declared in Terraform; RDS automatically creates the /aws/rds/instance/<instance-id>/<log-type> log groups.

Common pitfalls

  • Empty list still fails

    Terraform treats enabled_cloudwatch_logs_exports = [] the same as omitting the argument entirely. No log types are sent to the API, and the control reports a failure. You must include at least one valid value, for example ["error"].

  • SQL Server Audit logs require additional setup

    If your organization needs SQL Server Audit, configure it through a custom option group with the SQLSERVER_AUDIT option (via aws_db_option_group) and the relevant SQL Server settings. Don't add "audit" to enabled_cloudwatch_logs_exports for RDS SQL Server. The only valid CloudWatch export types are "error" and "agent".

  • CloudWatch log group retention defaults to never expire

    RDS auto-creates CloudWatch log groups with no expiration policy. Left alone, logs accumulate indefinitely and CloudWatch Logs storage costs grow with them. Pre-create the log groups in Terraform using aws_cloudwatch_log_group with retention_in_days set before enabling exports.

  • Engine filter matters

    Get the engine check wrong in a conditional module and this control silently misapplies. All four SQL Server variants must be handled explicitly: sqlserver-ee, sqlserver-se, sqlserver-ex, and sqlserver-web. A prefix match on sqlserver alone can collide with future engine names and produce unexpected behavior.

Audit evidence

Auditors expect AWS Config rule evaluation results showing each SQL Server RDS instance as COMPLIANT. Supporting evidence includes the RDS instance detail page in the AWS Console (under the "Logs & events" or "Configuration" tab) showing the published log types, or the output of aws rds describe-db-instances where the EnabledCloudwatchLogsExports array contains "error" and/or "agent".

CloudWatch Logs console screenshots showing active log groups under /aws/rds/instance/ with recent log streams confirm log delivery is working. Retention policy settings on those log groups show logs are preserved for the required period.

Framework-specific interpretation

Tool mappings

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

  • Compliance.tf Control: rds_db_instance_sql_server_logging_enabled

  • AWS Config Managed Rule: RDS_SQL_SERVER_LOGS_TO_CLOUDWATCH

  • Checkov Check: CKV_AWS_129

  • Powerpipe Control: aws_compliance.control.rds_db_instance_sql_server_logging_enabled

  • Prowler Check: rds_instance_integration_cloudwatch_logs

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

  • KICS Query: 8d7f7b8c-6c7c-40f8-baa6-62006c6c7b56

Last reviewed: 2026-03-09