OpenSearch domains should have fine-grained access control enabled
Fine-grained access control in OpenSearch lets you define index-level, document-level, and field-level permissions using roles mapped to IAM principals or an internal user database. Without it, any authenticated principal with domain-level access can read or modify all indices. This creates a flat access model where a compromised application credential exposes every index in the domain.
Enabling FGAC also activates the OpenSearch Dashboards security plugin, giving you audit logging of authentication events and a UI for managing roles. For domains holding PII, financial records, or log data from multiple tenants, the absence of FGAC makes least-privilege enforcement impossible at the data layer.
Retrofit consideration
Enabling fine-grained access control on an existing domain requires that encryption at rest, node-to-node encryption, and HTTPS enforcement are already enabled. If any are missing, you must enable them first, which can trigger a blue/green deployment. Existing application clients must then be updated to authenticate with either IAM credentials or the internal user database master user, which will break access patterns that relied on open or IP-based policies.
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 = "pcidss.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"
}
module "opensearch" {
source = "acscessentialeight.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"
}
module "opensearch" {
source = "nistcsfv11.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"
}
module "opensearch" {
source = "pcidssv321.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"
}
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"
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"
advanced_security_options = {
enabled = true
}
}
Use AWS provider resources directly. See docs for the resources involved: aws_opensearch_domain.
resource "aws_opensearch_domain" "this" {
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 = "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"]
}
advanced_security_options {
enabled = true
internal_user_database_enabled = true
master_user_options {
master_user_name = "admin"
master_user_password = "ChangeMe123!"
}
}
}
What this control checks
On the aws_opensearch_domain resource, advanced_security_options must be present with enabled = true. Inside that block, master_user_options must specify either master_user_arn for an IAM-based master user, or both master_user_name and master_user_password for the internal user database. When using the internal user database, also set internal_user_database_enabled = true. Fine-grained access control has prerequisites: encrypt_at_rest.enabled, node_to_node_encryption.enabled, and domain_endpoint_options.enforce_https must all be true. A domain missing any of these causes apply to fail with an API error. The control fails when advanced_security_options is omitted or enabled is false.
Common pitfalls
Prerequisites silently block enablement
Fine-grained access control requires
encrypt_at_rest.enabled,node_to_node_encryption.enabled, anddomain_endpoint_options.enforce_httpsall set totrue. If any is missing, Terraform surfaces an API error, but the message often doesn't clearly identify which prerequisite failed. Configure all three before or alongsideadvanced_security_optionsto avoid iterating through apply failures.Master user password in state file
Setting
master_user_nameandmaster_user_passworddirectly inmaster_user_optionsputs the password in plaintext in Terraform state. Usemaster_user_arnpointing to an IAM principal instead, or generate a password withrandom_password, store it viaaws_secretsmanager_secret_version, and mark it sensitive to at least limit exposure in logs and plan output.Overly broad domain access policy
Enabling FGAC does not automatically restrict the domain access policy. A resource-based policy with
"Principal": "*"and no conditions still lets any principal reach the endpoint; FGAC handles authorization from there, but you've left the front door open. Tighten the access policy to specific IAM principals or VPC-constrained sources.Blue/green deployment on existing domains
Enabling
advanced_security_optionson a running domain triggers a blue/green deployment. Depending on cluster size, that can take anywhere from 15 minutes to several hours. Plan a maintenance window, notify dependent teams, and test client authentication patterns after the cutover before declaring it done.
Audit evidence
AWS Config rule evaluation results showing each OpenSearch domain as compliant satisfy the primary evidence requirement, or equivalent CSPM output. The DescribeDomain API response should show AdvancedSecurityOptions.Enabled: true and AdvancedSecurityOptions.InternalUserDatabaseEnabled reflecting the chosen authentication strategy. Supporting evidence includes the domain's access policy document and OpenSearch Dashboards screenshots showing configured roles with index-level or document-level restrictions.
CloudTrail UpdateDomainConfig events confirm when FGAC was enabled and by whom. If the internal user database is in use, evidence that the master user password is stored in Secrets Manager with automatic rotation is also expected.
Framework-specific interpretation
PCI DSS v4.0: PCI DSS v4.0 Requirement 7 calls for restricting access to cardholder data by business need to know. FGAC addresses this at the data layer: index-level and document-level permissions mean individual roles see only the data they need. Requirement 7.2.1 specifically expects access control models tied to business need, and FGAC's role mapping against IAM principals or internal users is how you meet it.
Related controls
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
Compliance.tf Control:
opensearch_domain_fine_grained_access_enabledAWS Config Managed Rule:
OPENSEARCH_ACCESS_CONTROL_ENABLEDCheckov Check:
CKV2_AWS_52Powerpipe Control:
aws_compliance.control.opensearch_domain_fine_grained_access_enabledProwler Check:
opensearch_service_domains_access_control_enabledAWS Security Hub Control:
Opensearch.7
Last reviewed: 2026-03-09