OpenSearch domains should have at least three data nodes
An OpenSearch cluster with fewer than three data nodes or without zone awareness has a single point of failure at the AZ level. If the sole AZ, or one of two AZs hosting data nodes, goes down, the cluster loses quorum or becomes read-only, taking down search and analytics workloads with it.
Three data nodes across multiple AZs let the cluster redistribute shards automatically during an AZ failure. This is the minimum configuration AWS recommends for production OpenSearch clusters, and it's the threshold where fault tolerance becomes meaningful.
Retrofit consideration
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 "opensearch" {
source = "registry.compliance.tf/terraform-aws-modules/opensearch/aws"
version = ">=2.0.0,<3.0.0"
advanced_security_options = {
enabled = true
internal_user_database_enabled = true
master_user_options = {
master_user_name = "admin"
master_user_password = "P0fix-Test-2026!"
}
}
auto_tune_options = {
desired_state = "DISABLED"
}
cluster_config = {
dedicated_master_enabled = false
instance_count = 1
instance_type = "t3.small.search"
zone_awareness_enabled = false
}
domain_endpoint_options = {
enforce_https = true
tls_security_policy = "Policy-Min-TLS-1-2-2019-07"
}
domain_name = "abc123"
ebs_options = {
ebs_enabled = true
volume_size = 20
volume_type = "gp3"
}
engine_version = "OpenSearch_2.11"
}This control is enforced automatically with Compliance.tf modules. Start free trial
If you use terraform-aws-modules/opensearch/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 "opensearch" {
source = "terraform-aws-modules/opensearch/aws"
version = ">=2.0.0,<3.0.0"
advanced_security_options = {
enabled = true
internal_user_database_enabled = true
master_user_options = {
master_user_name = "admin"
master_user_password = "P0fix-Test-2026!"
}
}
auto_tune_options = {
desired_state = "DISABLED"
}
domain_endpoint_options = {
enforce_https = true
tls_security_policy = "Policy-Min-TLS-1-2-2019-07"
}
domain_name = "abc123"
ebs_options = {
ebs_enabled = true
volume_size = 20
volume_type = "gp3"
}
engine_version = "OpenSearch_2.11"
cluster_config = {
zone_awareness_enabled = true
}
}Use AWS provider resources directly. See docs for the resources involved: aws_opensearch_domain.
resource "aws_opensearch_domain" "this" {
advanced_security_options {
enabled = true
internal_user_database_enabled = true
master_user_options {
master_user_name = "admin"
master_user_password = "ChangeMe123!"
}
}
auto_tune_options {
desired_state = "DISABLED"
}
cognito_options {
enabled = true
identity_pool_id = "us-east-1:12345678-1234-1234-1234-123456789012"
role_arn = "arn:aws:iam::123456789012:role/example-role"
user_pool_id = "us-east-1_AbCdEfGhI"
}
domain_endpoint_options {
enforce_https = true
tls_security_policy = "Policy-Min-TLS-1-2-2019-07"
}
domain_name = "pofix-abc123"
ebs_options {
ebs_enabled = true
volume_size = 10
volume_type = "gp3"
}
encrypt_at_rest {
enabled = true
}
engine_version = "OpenSearch_2.11"
log_publishing_options {
cloudwatch_log_group_arn = local.es_log_group_arn
log_type = "AUDIT_LOGS"
}
log_publishing_options {
cloudwatch_log_group_arn = local.es_log_group_arn
log_type = "ES_APPLICATION_LOGS"
}
log_publishing_options {
cloudwatch_log_group_arn = local.es_log_group_arn
log_type = "SEARCH_SLOW_LOGS"
}
log_publishing_options {
cloudwatch_log_group_arn = local.es_log_group_arn
log_type = "INDEX_SLOW_LOGS"
}
node_to_node_encryption {
enabled = true
}
vpc_options {
security_group_ids = ["sg-12345678"]
subnet_ids = ["subnet-12345678", "subnet-12345678"]
}
cluster_config {
dedicated_master_enabled = false
instance_count = 4
instance_type = "t3.small.search"
zone_awareness_enabled = true
}
}What this control checks
In the aws_opensearch_domain resource, the cluster_config block must set instance_count to 3 or greater and zone_awareness_enabled to true. With zone_awareness_enabled = true, also configure the nested zone_awareness_config block with availability_zone_count set to 2 or 3 to control how many AZs the nodes span. A domain fails this control if instance_count is less than 3 or zone_awareness_enabled is false (or omitted, since it defaults to false). For three-AZ deployments, set availability_zone_count to 3 and instance_count to a multiple of 3 for even shard distribution.
Common pitfalls
Default instance_count is 1
instance_count defaults to 1 when omitted from cluster_config. Any domain created without an explicit value fails this control immediately, including test clusters that later get promoted to production without a Terraform review.
zone_awareness_enabled without zone_awareness_config
Setting zone_awareness_enabled = true without a zone_awareness_config block defaults to 2 AZs. This passes the control but may not match your resilience target. If you later increase instance_count to 6 expecting 3-AZ distribution, you get 3 nodes per AZ across only 2 AZs instead of 2 per AZ across 3.
Uneven instance_count across AZs
If instance_count is not evenly divisible by availability_zone_count, OpenSearch distributes nodes unevenly. Five nodes across 3 AZs places them 2-2-1, which means losing the AZ with 1 node is less impactful than losing one with 2. The control passes, but shard allocation becomes unbalanced.
Dedicated master nodes do not count as data nodes
Setting dedicated_master_enabled = true and dedicated_master_count = 3 does not satisfy this control. The check evaluates instance_count, which covers data nodes only. You need both dedicated masters and at least 3 data nodes.
Audit evidence
AWS Config rule evaluation results showing each OpenSearch domain as compliant, or a Security Hub findings export filtered to this control, are the most portable form of audit evidence. Console screenshots of the cluster configuration page showing data node count (3+) and the Zone Awareness or Multi-AZ with Standby setting are also acceptable for point-in-time checks. For continuous compliance, a CloudWatch dashboard or Config conformance pack report showing sustained compliance over the audit period is stronger than point-in-time screenshots.
Related controls
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
- Compliance.tf Control:
opensearch_domain_data_node_fault_tolerance - AWS Config Managed Rule:
OPENSEARCH_DATA_NODE_FAULT_TOLERANCE - Powerpipe Controls:
aws_compliance.control.es_domain_data_nodes_min_3,aws_compliance.control.opensearch_domain_data_node_fault_tolerance - Prowler Check:
opensearch_service_domains_fault_tolerant_data_nodes - AWS Security Hub Controls:
ES.6,Opensearch.6
Last reviewed: 2026-03-09