OpenSearch domains should be in a VPC
An OpenSearch domain outside a VPC gets a public endpoint reachable from the internet. Even with restrictive access policies, that exposure is real: IP-based policies can be misconfigured, and a single policy change can expose sensitive index data. VPC placement in private subnets adds a network-layer boundary that limits connectivity to resources within the VPC or connected networks.
Public OpenSearch endpoints also show up in DNS enumeration and internet scanning tools, making them visible targets.
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.
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"
}
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"
}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"
}
cluster_config {
instance_count = 1
instance_type = "t3.small.search"
zone_awareness_enabled = false
}
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 = "example-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"]
}
}What this control checks
The control checks that the aws_opensearch_domain resource includes a vpc_options block with subnet_ids, and that none of those subnets is public. A subnet counts as public when its route table sends 0.0.0.0/0 to an internet gateway. A domain fails when vpc_options is omitted or when any of its subnets is public. To pass, point subnet_ids at private subnets and set security_group_ids to groups that restrict inbound access; skipping it leaves the domain on the VPC default security group. When zone awareness is enabled, supply subnets matching the configured AZ count.
Common pitfalls
Public subnets fail, open security groups do not
Placing the domain in subnets with a route to an internet gateway fails the control. Security group rules are not evaluated, though, so a domain in private subnets with a security group allowing 0.0.0.0/0 inbound still passes. Keep aws_security_group ingress rules tight.
Subnet count must match Availability Zone count
If zone_awareness_enabled is true in the cluster_config block, you must supply subnets in two or three AZs depending on availability_zone_count. Providing a single subnet with zone awareness enabled causes a hard deployment error, not a soft failure. Terraform will surface this as an API error on apply.
Cannot convert public domain to VPC in place
AWS does not support switching an existing public OpenSearch domain to VPC mode via UpdateDomainConfig. Terraform will require resource replacement, which means data loss without a migration plan. Take manual snapshots before you start, or build an automated snapshot strategy into the cutover runbook.
VPC endpoint limits per domain
Each OpenSearch domain in a VPC provisions an ENI in each specified subnet. Those ENIs consume private IP addresses and count against the per-subnet ENI limit. Large clusters in small subnets (/28) can exhaust available IPs, causing node provisioning failures that are easy to misread as an OpenSearch issue rather than a networking one.
Audit evidence
Auditors will look for AWS Config results for the opensearch-in-vpc-only managed rule showing compliant evaluations across all in-scope OpenSearch domains. Console screenshots of each domain's "Networking" tab showing VPC, subnet, and security group assignments work as supporting point-in-time evidence. CloudTrail CreateDomain and UpdateDomainConfig events confirm that VPCOptions was specified at creation and has not since been removed.
For continuous compliance, a Security Hub report showing the opensearch_domain_in_vpc check passing across all accounts and regions rounds out the evidence package.
Framework-specific interpretation
PCI DSS v4.0: Requirement 1 calls for network controls that isolate cardholder data environment components from untrusted networks. A public OpenSearch endpoint means your CDE has an internet-facing component, which fails that isolation test. VPC placement helps meet Requirement 1, but it doesn't stand alone. Security groups and NACLs need to restrict traffic further before you can claim full coverage.
HIPAA Omnibus Rule 2013: 45 CFR 164.312 requires access controls and transmission security for ePHI. OpenSearch indexes can hold ePHI directly or serve queries against it, so the domain needs the same network boundary you'd apply to any other ePHI datastore. VPC placement is one way to satisfy that boundary requirement under the access control and transmission security provisions.
ISO/IEC 27001:2022: A.8.22 (Segregation of networks) says information services should be confined to defined network segments. Deploying OpenSearch inside a VPC removes it from the public internet and makes that segregation enforced at the network layer, not just at the policy layer.
Related controls
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
- Compliance.tf Control:
opensearch_domain_in_vpc - AWS Config Managed Rules:
ELASTICSEARCH_IN_VPC_ONLY,OPENSEARCH_IN_VPC_ONLY - Powerpipe Control:
aws_compliance.control.opensearch_domain_in_vpc
Last reviewed: 2026-10-03