Elasticsearch domain should send logs to CloudWatch
OpenSearch domains process search queries and index operations that surface performance bottlenecks, misconfigurations, and security incidents. Without CloudWatch log delivery, you lose visibility into slow queries, authentication failures, and cluster errors, and you have no signal when something goes wrong without manual investigation.
Centralized logging also supports retention requirements across frameworks covering cloud-hosted search workloads. CloudWatch Logs supports metric filters, alarms, and cross-account aggregation, so a single log publishing configuration enables automated detection of anomalous access patterns or degraded cluster health.
Retrofit consideration
Enabling log publishing on an existing domain requires a CloudWatch Logs resource policy granting OpenSearch permission to write to the target log group. Missing this policy causes silent delivery failures without any domain-level error to indicate the problem.
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" {
advanced_security_options {
enabled = true
internal_user_database_enabled = true
master_user_options {
master_user_name = "admin"
master_user_password = "ChangeMe123!"
}
}
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"]
}
}
What this control checks
This control validates that the aws_opensearch_domain resource includes one or more log_publishing_options blocks with enabled = true and a valid cloudwatch_log_group_arn. The log_type in each block must be one of INDEX_SLOW_LOGS, SEARCH_SLOW_LOGS, ES_APPLICATION_LOGS, or AUDIT_LOGS. A domain with no log_publishing_options blocks, or where all blocks have enabled = false, fails. The referenced log group must also exist, and a CloudWatch Logs resource policy must grant the OpenSearch service principal (es.amazonaws.com) logs:PutLogEvents and logs:CreateLogStream (and in some configurations, logs:CreateLogGroup) on the target log group. Without the resource policy, Terraform applies cleanly but log delivery silently fails.
Common pitfalls
Missing CloudWatch Logs resource policy
Log delivery silently fails if
aws_cloudwatch_log_resource_policyis missing, even though the domain configuration accepts thelog_publishing_optionsblock without error. Terraform doesn't enforce cross-resource dependency at plan time, so this gap won't surface until you go looking for logs that aren't there.Using legacy aws_elasticsearch_domain resource
Migrate to
aws_opensearch_domainif you're still using the legacyaws_elasticsearch_domainresource. Thelog_publishing_optionssyntax is similar between the two, but mixing resource types during migration creates state management confusion that's easier to avoid by completing the migration cleanly.Log group ARN format mismatch
Don't append wildcard suffixes to
cloudwatch_log_group_arn. Wildcards are valid in CloudWatch Logs resource policyResourceentries when targeting log streams, butcloudwatch_log_group_arnexpects a plain log group ARN. Conflating the two contexts is a common source of misconfiguration.Audit logs require fine-grained access control
Publishing
AUDIT_LOGSrequiresadvanced_security_optionswithenabled = trueon the domain. Without fine-grained access control, OpenSearch rejects the audit log configuration andterraform applyfails.
Audit evidence
AWS Config rule OPENSEARCH_LOGS_TO_CLOUDWATCH evaluation results showing compliant status across all domains are the primary evidence artifact. Console evidence is the domain's "Logs" tab showing each enabled log type with a linked log group ARN.
Supporting artifacts include CloudWatch Logs Insights queries showing actual log ingestion from the domain, CloudTrail events for UpdateDomainConfig or CreateDomain calls with LogPublishingOptions in the request parameters, and the CloudWatch Logs resource policy confirming the OpenSearch service principal has write access.
Framework-specific interpretation
SOC 2: CC7.2 and CC7.3 expect evidence that system components are monitored for anomalies. During a Type II engagement, auditors look for CloudWatch log ingestion from OpenSearch as proof that continuous monitoring is actually in place, not just configured.
PCI DSS v4.0: For environments in CDE scope, Requirement 10.2 calls for capturing audit events across all in-scope system components and 10.3 calls for protecting them. OpenSearch domains handling cardholder data need CloudWatch log publishing to cover both. Retention requirements under 10.5 apply to the destination log group as well.
HIPAA Omnibus Rule 2013: 45 CFR 164.312(b) requires audit controls capable of recording and examining activity in systems containing ePHI. For OpenSearch clusters indexing health data, CloudWatch log delivery is how you produce that audit trail.
NIST SP 800-53 Rev 5: AU-2, AU-3, AU-6, and SI-4 together cover event logging, record content, audit review, and system monitoring; CloudWatch log delivery from OpenSearch is how you address all four without separate tooling.
NIST Cybersecurity Framework v2.0: OpenSearch log delivery to CloudWatch feeds directly into DE.CM continuous monitoring and supports ID.AM asset visibility for anything your clusters index. Both functions expect this level of telemetry from operational infrastructure.
FedRAMP Moderate Baseline Rev 4: AU-2 and AU-3 require audit logging across all system components, and AU-6 requires mechanisms for review and analysis of those records. OpenSearch domains processing federal data satisfy all three by forwarding logs to CloudWatch.
Related controls
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
Compliance.tf Control:
es_domain_logs_to_cloudwatchAWS Config Managed Rule:
ELASTICSEARCH_LOGS_TO_CLOUDWATCHCheckov Check:
CKV_AWS_84Powerpipe Controls:
aws_compliance.control.es_domain_logs_to_cloudwatch,aws_compliance.control.opensearch_domain_logs_to_cloudwatchProwler Check:
opensearch_service_domains_cloudwatch_logging_enabledAWS Security Hub Controls:
ES.4,Opensearch.4Trivy Check:
AWS-0042
Last reviewed: 2026-03-09