Skip to content

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_policy is missing, even though the domain configuration accepts the log_publishing_options block 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_domain if you're still using the legacy aws_elasticsearch_domain resource. The log_publishing_options syntax 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 policy Resource entries when targeting log streams, but cloudwatch_log_group_arn expects a plain log group ARN. Conflating the two contexts is a common source of misconfiguration.

  • Audit logs require fine-grained access control

    Publishing AUDIT_LOGS requires advanced_security_options with enabled = true on the domain. Without fine-grained access control, OpenSearch rejects the audit log configuration and terraform apply fails.

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.

Tool mappings

Use these identifiers to cross-reference this control across tools, reports, and evidence.

  • Compliance.tf Control: es_domain_logs_to_cloudwatch

  • AWS Config Managed Rule: ELASTICSEARCH_LOGS_TO_CLOUDWATCH

  • Checkov Check: CKV_AWS_84

  • Powerpipe Controls: aws_compliance.control.es_domain_logs_to_cloudwatch, aws_compliance.control.opensearch_domain_logs_to_cloudwatch

  • Prowler Check: opensearch_service_domains_cloudwatch_logging_enabled

  • AWS Security Hub Controls: ES.4, Opensearch.4

  • Trivy Check: AWS-0042

Last reviewed: 2026-03-09