RDS for PostgreSQL DB instances should publish logs to CloudWatch Logs
PostgreSQL logs contain connection attempts, query errors, deadlocks, and authentication failures. Without forwarding these to CloudWatch Logs, you lose centralized visibility into database activity. Incident responders cannot correlate application errors with database events, and retention depends entirely on the RDS instance lifecycle rather than a durable log store.
Exporting to CloudWatch also unlocks metric filters, alarms, and CloudWatch Logs Insights for ad-hoc investigation. If the instance is terminated or storage fills up, local logs may be lost permanently.
Retrofit consideration
apply_immediately setting, this may cause a brief connectivity interruption during the change window.Implementation
Choose the approach that matches how you manage Terraform.
Use the compliance.tf module to enforce this control by default. See get started with compliance.tf.
module "rds" {
source = "registry.compliance.tf/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"]
}This control is enforced automatically with Compliance.tf modules. Start free trial
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 = ["postgresql", "upgrade"]
engine = "postgres"
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
For aws_db_instance resources with engine = "postgres", enabled_cloudwatch_logs_exports must include the required log types. At minimum, "postgresql" must be present; "upgrade" may also be required depending on the control's logTypes parameter. A passing configuration looks like enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"]. It fails if the argument is omitted, set to an empty list, or missing one of the expected values. No additional IAM configuration is required: RDS automatically creates the necessary CloudWatch Logs log groups and streams when exports are enabled.
Common pitfalls
Omitting upgrade log type
If the control's logTypes parameter expects both "postgresql" and "upgrade", including only "postgresql" still triggers a failure. Check the parameterization before assuming the default list is sufficient.
Engine mismatch with Aurora PostgreSQL
This control targets aws_db_instance resources with engine = "postgres", not Aurora PostgreSQL clusters. Aurora PostgreSQL logging is configured on aws_rds_cluster with enabled_cloudwatch_logs_exports, which is a separate control surface evaluated independently.
Insufficient log_min_messages parameter
Enabling log export does not control what PostgreSQL actually logs. If log_statement, log_min_duration_statement, or log_connections are unset in the associated aws_db_parameter_group, the logs flowing into CloudWatch may contain very little useful signal.
CloudWatch Logs retention defaults to never expire
Declare the log group explicitly in Terraform using aws_cloudwatch_log_group and set retention_in_days before enabling exports. RDS reuses an existing log group rather than creating a new one, so without an explicit resource the group defaults to indefinite retention and storage costs grow unbounded.
Audit evidence
AWS Config evaluation results showing all RDS PostgreSQL instances as compliant for log export configuration are the primary evidence. Supporting that, the CloudWatch Logs console should show active log groups under /aws/rds/instance/<instance-id>/postgresql with recent log streams confirming logs are flowing. The RDS console's "Configuration" tab for each instance lists the published log types under "Log exports."
For deeper assurance, CloudTrail ModifyDBInstance events confirm when log exports were enabled. CloudWatch Logs retention policies on the relevant log groups demonstrate the organization is retaining logs for the required duration.
Related controls
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
- Compliance.tf Control:
rds_db_instance_postgres_logging_enabled - AWS Config Managed Rule:
RDS_POSTGRESQL_LOGS_TO_CLOUDWATCH - Checkov Check:
CKV_AWS_129 - Powerpipe Control:
aws_compliance.control.rds_db_instance_postgres_logging_enabled - Prowler Check:
rds_instance_integration_cloudwatch_logs - AWS Security Hub Controls:
RDS.36,RDS.9
Last reviewed: 2026-03-09