Redshift clusters should prohibit public access
A publicly accessible Redshift cluster exposes your data warehouse directly to the internet. Even with strong credentials and security group rules, this unnecessarily widens the attack surface. Credential stuffing, brute-force attempts, and zero-day exploits against the Redshift endpoint all become viable threats when the cluster has a public IP.
Private clusters force access through VPC-internal paths or private connectivity such as VPN or Direct Connect, giving you layered network controls that don't depend solely on authentication.
Retrofit consideration
Changing publicly_accessible from true to false on a running cluster triggers a modification that updates DNS resolution. Applications connecting via the public endpoint can break if private connectivity is not in place first. Reconfigure clients to use the private endpoint or establish VPN/Direct Connect before making the change.
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 "redshift" {
source = "soc2.compliance.tf/terraform-aws-modules/redshift/aws"
version = ">=7.0.0,<8.0.0"
automated_snapshot_retention_period = 7
cluster_identifier = "abc123"
create_cloudwatch_log_group = true
database_name = "mydb"
logging = {
log_destination_type = "cloudwatch"
log_exports = ["connectionlog", "userlog", "useractivitylog"]
}
master_password_wo = "change-me-in-production"
master_username = "admin"
node_type = "ra3.xlplus"
number_of_nodes = 2
subnet_ids = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
vpc_security_group_ids = ["sg-12345678"]
}
module "redshift" {
source = "pcidss.compliance.tf/terraform-aws-modules/redshift/aws"
version = ">=7.0.0,<8.0.0"
automated_snapshot_retention_period = 7
cluster_identifier = "abc123"
create_cloudwatch_log_group = true
database_name = "mydb"
logging = {
log_destination_type = "cloudwatch"
log_exports = ["connectionlog", "userlog", "useractivitylog"]
}
master_password_wo = "change-me-in-production"
master_username = "admin"
node_type = "ra3.xlplus"
number_of_nodes = 2
subnet_ids = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
vpc_security_group_ids = ["sg-12345678"]
}
module "redshift" {
source = "hipaa.compliance.tf/terraform-aws-modules/redshift/aws"
version = ">=7.0.0,<8.0.0"
automated_snapshot_retention_period = 7
cluster_identifier = "abc123"
create_cloudwatch_log_group = true
database_name = "mydb"
logging = {
log_destination_type = "cloudwatch"
log_exports = ["connectionlog", "userlog", "useractivitylog"]
}
master_password_wo = "change-me-in-production"
master_username = "admin"
node_type = "ra3.xlplus"
number_of_nodes = 2
subnet_ids = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
vpc_security_group_ids = ["sg-12345678"]
}
module "redshift" {
source = "nist80053.compliance.tf/terraform-aws-modules/redshift/aws"
version = ">=7.0.0,<8.0.0"
automated_snapshot_retention_period = 7
cluster_identifier = "abc123"
create_cloudwatch_log_group = true
database_name = "mydb"
logging = {
log_destination_type = "cloudwatch"
log_exports = ["connectionlog", "userlog", "useractivitylog"]
}
master_password_wo = "change-me-in-production"
master_username = "admin"
node_type = "ra3.xlplus"
number_of_nodes = 2
subnet_ids = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
vpc_security_group_ids = ["sg-12345678"]
}
module "redshift" {
source = "fedrampmoderate.compliance.tf/terraform-aws-modules/redshift/aws"
version = ">=7.0.0,<8.0.0"
automated_snapshot_retention_period = 7
cluster_identifier = "abc123"
create_cloudwatch_log_group = true
database_name = "mydb"
logging = {
log_destination_type = "cloudwatch"
log_exports = ["connectionlog", "userlog", "useractivitylog"]
}
master_password_wo = "change-me-in-production"
master_username = "admin"
node_type = "ra3.xlplus"
number_of_nodes = 2
subnet_ids = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
vpc_security_group_ids = ["sg-12345678"]
}
module "redshift" {
source = "cisv80ig1.compliance.tf/terraform-aws-modules/redshift/aws"
version = ">=7.0.0,<8.0.0"
automated_snapshot_retention_period = 7
cluster_identifier = "abc123"
create_cloudwatch_log_group = true
database_name = "mydb"
logging = {
log_destination_type = "cloudwatch"
log_exports = ["connectionlog", "userlog", "useractivitylog"]
}
master_password_wo = "change-me-in-production"
master_username = "admin"
node_type = "ra3.xlplus"
number_of_nodes = 2
subnet_ids = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
vpc_security_group_ids = ["sg-12345678"]
}
module "redshift" {
source = "nist800171.compliance.tf/terraform-aws-modules/redshift/aws"
version = ">=7.0.0,<8.0.0"
automated_snapshot_retention_period = 7
cluster_identifier = "abc123"
create_cloudwatch_log_group = true
database_name = "mydb"
logging = {
log_destination_type = "cloudwatch"
log_exports = ["connectionlog", "userlog", "useractivitylog"]
}
master_password_wo = "change-me-in-production"
master_username = "admin"
node_type = "ra3.xlplus"
number_of_nodes = 2
subnet_ids = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
vpc_security_group_ids = ["sg-12345678"]
}
module "redshift" {
source = "cisacyberessentials.compliance.tf/terraform-aws-modules/redshift/aws"
version = ">=7.0.0,<8.0.0"
automated_snapshot_retention_period = 7
cluster_identifier = "abc123"
create_cloudwatch_log_group = true
database_name = "mydb"
logging = {
log_destination_type = "cloudwatch"
log_exports = ["connectionlog", "userlog", "useractivitylog"]
}
master_password_wo = "change-me-in-production"
master_username = "admin"
node_type = "ra3.xlplus"
number_of_nodes = 2
subnet_ids = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
vpc_security_group_ids = ["sg-12345678"]
}
module "redshift" {
source = "nydfs23.compliance.tf/terraform-aws-modules/redshift/aws"
version = ">=7.0.0,<8.0.0"
automated_snapshot_retention_period = 7
cluster_identifier = "abc123"
create_cloudwatch_log_group = true
database_name = "mydb"
logging = {
log_destination_type = "cloudwatch"
log_exports = ["connectionlog", "userlog", "useractivitylog"]
}
master_password_wo = "change-me-in-production"
master_username = "admin"
node_type = "ra3.xlplus"
number_of_nodes = 2
subnet_ids = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
vpc_security_group_ids = ["sg-12345678"]
}
module "redshift" {
source = "ffiec.compliance.tf/terraform-aws-modules/redshift/aws"
version = ">=7.0.0,<8.0.0"
automated_snapshot_retention_period = 7
cluster_identifier = "abc123"
create_cloudwatch_log_group = true
database_name = "mydb"
logging = {
log_destination_type = "cloudwatch"
log_exports = ["connectionlog", "userlog", "useractivitylog"]
}
master_password_wo = "change-me-in-production"
master_username = "admin"
node_type = "ra3.xlplus"
number_of_nodes = 2
subnet_ids = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
vpc_security_group_ids = ["sg-12345678"]
}
module "redshift" {
source = "acscessentialeight.compliance.tf/terraform-aws-modules/redshift/aws"
version = ">=7.0.0,<8.0.0"
automated_snapshot_retention_period = 7
cluster_identifier = "abc123"
create_cloudwatch_log_group = true
database_name = "mydb"
logging = {
log_destination_type = "cloudwatch"
log_exports = ["connectionlog", "userlog", "useractivitylog"]
}
master_password_wo = "change-me-in-production"
master_username = "admin"
node_type = "ra3.xlplus"
number_of_nodes = 2
subnet_ids = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
vpc_security_group_ids = ["sg-12345678"]
}
module "redshift" {
source = "cfrpart11.compliance.tf/terraform-aws-modules/redshift/aws"
version = ">=7.0.0,<8.0.0"
automated_snapshot_retention_period = 7
cluster_identifier = "abc123"
create_cloudwatch_log_group = true
database_name = "mydb"
logging = {
log_destination_type = "cloudwatch"
log_exports = ["connectionlog", "userlog", "useractivitylog"]
}
master_password_wo = "change-me-in-production"
master_username = "admin"
node_type = "ra3.xlplus"
number_of_nodes = 2
subnet_ids = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
vpc_security_group_ids = ["sg-12345678"]
}
module "redshift" {
source = "rbicybersecurity.compliance.tf/terraform-aws-modules/redshift/aws"
version = ">=7.0.0,<8.0.0"
automated_snapshot_retention_period = 7
cluster_identifier = "abc123"
create_cloudwatch_log_group = true
database_name = "mydb"
logging = {
log_destination_type = "cloudwatch"
log_exports = ["connectionlog", "userlog", "useractivitylog"]
}
master_password_wo = "change-me-in-production"
master_username = "admin"
node_type = "ra3.xlplus"
number_of_nodes = 2
subnet_ids = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
vpc_security_group_ids = ["sg-12345678"]
}
module "redshift" {
source = "rbiitfnbfc.compliance.tf/terraform-aws-modules/redshift/aws"
version = ">=7.0.0,<8.0.0"
automated_snapshot_retention_period = 7
cluster_identifier = "abc123"
create_cloudwatch_log_group = true
database_name = "mydb"
logging = {
log_destination_type = "cloudwatch"
log_exports = ["connectionlog", "userlog", "useractivitylog"]
}
master_password_wo = "change-me-in-production"
master_username = "admin"
node_type = "ra3.xlplus"
number_of_nodes = 2
subnet_ids = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
vpc_security_group_ids = ["sg-12345678"]
}
module "redshift" {
source = "fedramplow.compliance.tf/terraform-aws-modules/redshift/aws"
version = ">=7.0.0,<8.0.0"
automated_snapshot_retention_period = 7
cluster_identifier = "abc123"
create_cloudwatch_log_group = true
database_name = "mydb"
logging = {
log_destination_type = "cloudwatch"
log_exports = ["connectionlog", "userlog", "useractivitylog"]
}
master_password_wo = "change-me-in-production"
master_username = "admin"
node_type = "ra3.xlplus"
number_of_nodes = 2
subnet_ids = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
vpc_security_group_ids = ["sg-12345678"]
}
module "redshift" {
source = "hipaasecurity2003.compliance.tf/terraform-aws-modules/redshift/aws"
version = ">=7.0.0,<8.0.0"
automated_snapshot_retention_period = 7
cluster_identifier = "abc123"
create_cloudwatch_log_group = true
database_name = "mydb"
logging = {
log_destination_type = "cloudwatch"
log_exports = ["connectionlog", "userlog", "useractivitylog"]
}
master_password_wo = "change-me-in-production"
master_username = "admin"
node_type = "ra3.xlplus"
number_of_nodes = 2
subnet_ids = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
vpc_security_group_ids = ["sg-12345678"]
}
module "redshift" {
source = "nistcsfv11.compliance.tf/terraform-aws-modules/redshift/aws"
version = ">=7.0.0,<8.0.0"
automated_snapshot_retention_period = 7
cluster_identifier = "abc123"
create_cloudwatch_log_group = true
database_name = "mydb"
logging = {
log_destination_type = "cloudwatch"
log_exports = ["connectionlog", "userlog", "useractivitylog"]
}
master_password_wo = "change-me-in-production"
master_username = "admin"
node_type = "ra3.xlplus"
number_of_nodes = 2
subnet_ids = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
vpc_security_group_ids = ["sg-12345678"]
}
module "redshift" {
source = "nist80053rev4.compliance.tf/terraform-aws-modules/redshift/aws"
version = ">=7.0.0,<8.0.0"
automated_snapshot_retention_period = 7
cluster_identifier = "abc123"
create_cloudwatch_log_group = true
database_name = "mydb"
logging = {
log_destination_type = "cloudwatch"
log_exports = ["connectionlog", "userlog", "useractivitylog"]
}
master_password_wo = "change-me-in-production"
master_username = "admin"
node_type = "ra3.xlplus"
number_of_nodes = 2
subnet_ids = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
vpc_security_group_ids = ["sg-12345678"]
}
module "redshift" {
source = "pcidssv321.compliance.tf/terraform-aws-modules/redshift/aws"
version = ">=7.0.0,<8.0.0"
automated_snapshot_retention_period = 7
cluster_identifier = "abc123"
create_cloudwatch_log_group = true
database_name = "mydb"
logging = {
log_destination_type = "cloudwatch"
log_exports = ["connectionlog", "userlog", "useractivitylog"]
}
master_password_wo = "change-me-in-production"
master_username = "admin"
node_type = "ra3.xlplus"
number_of_nodes = 2
subnet_ids = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
vpc_security_group_ids = ["sg-12345678"]
}
If you use terraform-aws-modules/redshift/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 "redshift" {
source = "terraform-aws-modules/redshift/aws"
version = ">=7.0.0,<8.0.0"
automated_snapshot_retention_period = 7
cluster_identifier = "abc123"
create_cloudwatch_log_group = true
database_name = "mydb"
logging = {
log_destination_type = "cloudwatch"
log_exports = ["connectionlog", "userlog", "useractivitylog"]
}
master_password_wo = "change-me-in-production"
master_username = "admin"
node_type = "ra3.xlplus"
number_of_nodes = 2
subnet_ids = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
vpc_security_group_ids = ["sg-12345678"]
publicly_accessible = false
}
Use AWS provider resources directly. See docs for the resources involved: aws_redshift_cluster.
resource "aws_redshift_cluster" "this" {
automated_snapshot_retention_period = 7
cluster_identifier = "pofix-abc123"
cluster_subnet_group_name = "example-redshift-subnet-group"
master_password = "ChangeMe123!"
master_username = "admin"
node_type = "ra3.large"
skip_final_snapshot = true
publicly_accessible = false
}
What this control checks
This control validates that each aws_redshift_cluster resource has publicly_accessible set to false. It fails when the argument is omitted or set to true. The cluster should also reside in a subnet group (cluster_subnet_group_name) backed by private subnets with no internet gateway route, though this control checks only the publicly_accessible flag.
Common pitfalls
Do not rely on implicit behavior
Set
publicly_accessible = falseexplicitly on everyaws_redshift_clusterresource. Omitting the argument doesn't guarantee private access; the default behavior depends on subnet type and can result in unintended exposure.Subnet group with public subnets
Setting
publicly_accessible = falseblocks public IP assignment, but if the cluster's subnet group references subnets with an internet gateway route, the security posture is weaker than it appears. The flag is necessary but not sufficient; back it with private subnets.Elastic IP association after creation
Console-driven changes can flip
publicly_accessibletotruewithout any Terraform modification. Runningaws redshift modify-cluster --publicly-accessibleor editing the setting in the console accomplishes this silently from Terraform's perspective. AWS Config continuous evaluation catches the drift, but infrequentterraform planruns will miss it until the next run.Snapshot restore creates new cluster
Restoring from a snapshot via
aws_redshift_clusterwithsnapshot_identifierdoes not carry forward the source cluster's network settings. The restored cluster gets whateverpublicly_accessiblevalue is in the Terraform config, which means an omission here exposes a freshly restored cluster.
Audit evidence
Auditors expect AWS Config evaluation results for redshift-cluster-public-access-check showing all clusters as compliant. Console screenshots of each cluster's Properties tab with 'Publicly accessible' set to 'No' work as direct evidence. A continuous compliance report from Security Hub or a CSPM tool showing no public Redshift clusters over the audit period covers the ongoing monitoring requirement. CloudTrail logs for ModifyCluster API calls with the PubliclyAccessible parameter show when the setting was changed and by whom.
Framework-specific interpretation
SOC 2: CC6.1 and CC6.6 expect access to information assets to go through controlled, authenticated paths. Keeping Redshift off the public internet is a direct implementation of that boundary requirement.
PCI DSS v4.0: Requirement 1.3 restricts inbound and outbound traffic to what the cardholder data environment actually needs. A public Redshift endpoint is hard to justify under that standard, and compensating controls are harder to document than simply disabling the flag.
HIPAA Omnibus Rule 2013: A Redshift cluster holding ePHI and reachable from the open internet conflicts directly with 164.312(a)(1), the access control standard. HIPAA's technical safeguards require network-layer controls on ePHI access, not just authentication at the application layer.
NIST SP 800-53 Rev 5: SC-7 and AC-4 are the relevant controls. SC-7 requires a managed boundary between the cluster and external networks; AC-4 restricts information flow to authorized internal paths. Disabling public access addresses both.
FedRAMP Moderate Baseline Rev 4: At the Moderate baseline, SC-7 says federal information systems should not expose database services to public networks. All access should route through authorized boundary protections, which means VPN or Direct Connect, not a public endpoint.
Related controls
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
Compliance.tf Control:
redshift_cluster_prohibit_public_accessAWS Config Managed Rule:
REDSHIFT_CLUSTER_PUBLIC_ACCESS_CHECKCheckov Check:
CKV_AWS_87Powerpipe Control:
aws_compliance.control.redshift_cluster_prohibit_public_accessProwler Check:
redshift_cluster_public_accessAWS Security Hub Control:
Redshift.1KICS Query:
af173fde-95ea-4584-b904-bb3923ac4bda
Last reviewed: 2026-03-09