OpenSearch domains should have audit logging enabled
Audit logs record authentication attempts, access to data, and changes to the OpenSearch domain's security configuration. Without them, you lose the ability to detect unauthorized access, investigate incidents, or prove to regulators that access patterns were reviewed. Turning audit logging off saves almost nothing in cost but creates a significant blind spot.
Fine-grained access control and audit logging work together. The former gates who can do what; the latter records who actually did what. Disabling either half undermines the security model OpenSearch provides.
Retrofit consideration
Audit logging requires fine-grained access control (advanced_security_options) to be enabled first. On a domain created without it, enabling advanced_security_options may trigger a blue/green deployment and requires configuring a master user, which can disrupt existing authentication flows.
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 = "soc2.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 = "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 = "nydfs23.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 = "acscism2023.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 = "cfrpart11.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"
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 = "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"]
}
}
What this control checks
The aws_opensearch_domain resource must include a log_publishing_options block with log_type set to "AUDIT_LOGS" and cloudwatch_log_group_arn pointing to a valid CloudWatch log group. The enabled argument within that block must be true (or omitted, since it defaults to true). Audit logging also requires advanced_security_options with enabled = true and a configured master user: either master_user_arn or master_user_name/master_user_password. A domain that lacks the AUDIT_LOGS log publishing option, or has it set with enabled = false, fails this control. The referenced CloudWatch log group must have a resource policy allowing OpenSearch to write to it, typically configured via aws_cloudwatch_log_resource_policy.
Common pitfalls
Missing advanced_security_options prerequisite
Audit logging requires fine-grained access control. If
advanced_security_options.enabledisfalseor absent, the OpenSearch API rejects the audit log configuration outright. Terraform surfaces this as a validation error at apply time, not plan time, which catches CI pipelines off guard if the prerequisite isn't already in state.CloudWatch log resource policy missing or too narrow
Without an
aws_cloudwatch_log_resource_policythat allows the OpenSearch service principal to create log streams and put log events, log delivery silently fails. The domain shows audit logging as configured, no Terraform error appears, but no records arrive in the log group. Always verify delivery by querying the log group after apply, not just checking the resource configuration.Confusing log_publishing_options log types
The
aws_opensearch_domainresource accepts multiplelog_publishing_optionsblocks for differentlog_typevalues:INDEX_SLOW_LOGS,SEARCH_SLOW_LOGS,ES_APPLICATION_LOGS, andAUDIT_LOGS. Having the others enabled does nothing for this control. You need an explicit block withlog_type = "AUDIT_LOGS".Legacy aws_elasticsearch_domain resource
Older configurations may still use the deprecated
aws_elasticsearch_domainresource. This control applies equally to it, but the argument structure differs slightly between providers. Migrate toaws_opensearch_domainto eliminate provider deprecation warnings and get consistent behavior across Terraform runs.
Audit evidence
The AWS Config rule OPENSEARCH_AUDIT_LOGGING_ENABLED showing compliant evaluations across all in-scope domains is the baseline expectation. Console evidence from the OpenSearch domain's "Logs" tab, showing audit logging enabled and the target CloudWatch log group ARN, strengthens the case. CloudWatch Logs Insights queries confirming that audit log data is actually arriving (not just configured) are the strongest proof. The log group's retention policy should match organizational or regulatory requirements.
Framework-specific interpretation
SOC 2: CC7.2 and CC7.3 expect anomaly detection and event evaluation against established baselines. OpenSearch audit logs are the evidence trail that access to indexed data was monitored, which SOC 2 auditors want under the Security and Availability trust service criteria.
PCI DSS v4.0: Requirements 10.2.1 and 10.2.2 call for records of individual user access to in-scope system components and cardholder data. For anything indexed in OpenSearch, audit logs capture authentication events and data access patterns. PCI examiners will ask to see both the configuration and evidence that records are actually landing in CloudWatch.
Related controls
Elasticsearch domain error logging to CloudWatch Logs should be enabled
Elasticsearch domains should have internal user database enabled
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
Compliance.tf Control:
opensearch_domain_audit_logging_enabledAWS Config Managed Rule:
OPENSEARCH_AUDIT_LOGGING_ENABLEDCheckov Check:
CKV_AWS_317Powerpipe Controls:
aws_compliance.control.es_domain_audit_logging_enabled,aws_compliance.control.opensearch_domain_audit_logging_enabledProwler Check:
opensearch_service_domains_audit_logging_enabledAWS Security Hub Controls:
ES.5,Opensearch.5Trivy Check:
AWS-0042
Last reviewed: 2026-03-09