Elasticsearch domains should have internal user database enabled
The internal user database gives you a self-contained authentication layer inside the OpenSearch domain. Without it, you depend entirely on external identity providers or IAM for every dashboard login and API call, which can leave you locked out during federation outages or IAM misconfigurations.
Having the internal user database enabled also lets you create service accounts with scoped permissions for index-level operations, automated ingestion pipelines, and cross-cluster replication. That local fallback is especially useful during incident response, when you need fast, reliable access to logs stored in OpenSearch without waiting on an IdP or a broken IAM policy to get sorted out.
Retrofit consideration
Enabling fine-grained access control on an existing domain requires a blue/green deployment. AWS handles this automatically, but it can take 20 minutes or more depending on cluster size and will cause brief connectivity interruptions during cutover. You also need to set a master user before running apply, so have a credential storage plan in place first.
Implementation
Choose the approach that matches how you manage Terraform.
Use AWS provider resources directly. See docs for the resources involved: aws_elasticsearch_domain.
resource "aws_elasticsearch_domain" "this" {
cluster_config {
dedicated_master_count = 3
dedicated_master_enabled = true
dedicated_master_type = "m5.large.elasticsearch"
instance_count = 3
instance_type = "m5.large.elasticsearch"
zone_awareness_config {
availability_zone_count = 3
}
zone_awareness_enabled = true
}
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"
}
elasticsearch_version = "7.10"
encrypt_at_rest {
enabled = true
}
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", "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
This control validates that the aws_opensearch_domain resource (or legacy aws_elasticsearch_domain) has its advanced_security_options block configured with both enabled = true and internal_user_database_enabled = true. When internal_user_database_enabled is true, you must also provide a master_user_options block with master_user_name and master_user_password. A domain that omits advanced_security_options entirely, sets enabled = false, or sets internal_user_database_enabled = false fails this control. Note that fine-grained access control also requires node_to_node_encryption with enabled = true, encrypt_at_rest with enabled = true, and HTTPS enforcement via domain_endpoint_options with enforce_https = true.
Common pitfalls
Fine-grained access control prerequisites
Miss any of the three prerequisites and the apply fails.
node_to_node_encryption,encrypt_at_rest, and HTTPS enforcement viaenforce_httpsindomain_endpoint_optionsmust all be enabled alongsideinternal_user_database_enabled = true. OpenSearch treats all four as a package; you can't enable fine-grained access control without satisfying the rest.Master user password in state file
Terraform stores
master_user_passwordin plaintext in state. Reference it from Secrets Manager or asensitivevariable rather than hardcoding it in.tffiles, and confirm your state backend (S3 or Terraform Cloud) encrypts at rest. Get this wrong and the credential is readable to anyone with state access.Retrofit triggers blue/green deployment
Plan this change in a maintenance window. Enabling
advanced_security_optionson an existing domain kicks off an AWS-managed blue/green deployment that can run 20 minutes or longer depending on cluster size, with brief connectivity interruptions during cutover. It is not a live, in-place configuration change.Deprecated resource name
The
aws_elasticsearch_domainresource is deprecated in favor ofaws_opensearch_domain. Both supportadvanced_security_options, but new deployments should use the OpenSearch resource type. Terraform won't migrate existing resources between types automatically, so switching an existing resource requires a state move or a destroy-and-recreate.
Audit evidence
Auditors will check AWS Config rule evaluation results showing compliant status for each OpenSearch domain, or equivalent CSPM findings. Console screenshots of the domain's security configuration page showing "Internal user database" as the master user source under fine-grained access control work as direct visual evidence.
CloudTrail logs for CreateDomain or UpdateDomainConfig API calls should show AdvancedSecurityOptions.InternalUserDatabaseEnabled set to true in the request parameters. The DescribeDomain API response confirms the current state of this setting per domain.
Framework-specific interpretation
Related controls
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
Compliance.tf Control:
es_domain_internal_user_database_enabledCheckov Check:
CKV2_AWS_52Powerpipe Control:
aws_compliance.control.es_domain_internal_user_database_enabledProwler Check:
opensearch_service_domains_internal_user_database_enabled
Last reviewed: 2026-03-09