Skip to content

Elasticsearch domains should have cognito authentication enabled

Without Cognito authentication, Kibana and OpenSearch Dashboards are either publicly accessible or rely on IP-based or proxy-based controls that have no concept of individual user identity. Cognito provides user pool authentication with MFA, password policies, and federation via SAML or OIDC providers, giving you traceable per-user access to the dashboard.

Enabling Cognito also lets you assign IAM roles through identity pool mappings, which opens up fine-grained access control over Elasticsearch indices and API operations. Open or IP-restricted dashboard access makes it nearly impossible to attribute queries to specific individuals or enforce least-privilege access patterns.

Retrofit consideration

Enabling Cognito on an existing domain requires a pre-configured Cognito User Pool, Identity Pool, and an IAM role with the AmazonOpenSearchServiceCognitoAccess policy already in place before you run terraform apply. Existing users with bookmarked Kibana URLs will hit a Cognito login page on their next visit. Coordinate with those teams before the change goes in.

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
  }

  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"]
  }

  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"
  }
}

What this control checks

The policy checks that aws_opensearch_domain (or the legacy aws_elasticsearch_domain) includes a cognito_options block with enabled = true. Four arguments are required inside the block: enabled must be true, user_pool_id must reference a valid Cognito User Pool, identity_pool_id must reference a valid Cognito Identity Pool, and role_arn must point to an IAM role with the AmazonOpenSearchServiceCognitoAccess managed policy attached. Omitting the block entirely or setting enabled = false both fail. The role_arn target should be a separate aws_iam_role resource with an sts:AssumeRole trust policy for es.amazonaws.com.

Common pitfalls

  • Missing IAM role trust policy for es.amazonaws.com

    Domain updates fail with an access denied error if the IAM role passed to role_arn trusts cognito-identity.amazonaws.com but not es.amazonaws.com. The role needs an explicit sts:AssumeRole trust policy for es.amazonaws.com. If you're creating it with aws_iam_role, verify the assume_role_policy document before running terraform apply or the apply will fail mid-update.

  • Cognito pool region mismatch

    user_pool_id and identity_pool_id must resolve to pools in the same region as each other. Cross-region references fail with a validation error at apply time, and the error message doesn't always make the region mismatch obvious.

  • Using deprecated aws_elasticsearch_domain resource

    If you're migrating from aws_elasticsearch_domain to aws_opensearch_domain, carry the cognito_options block over explicitly. Terraform import and state mv operations don't validate that configuration arguments survived the move, so it's easy to end up with a domain that passes terraform plan but fails this control.

  • Identity Pool unauthenticated access left enabled

    Leave allow_unauthenticated_identities = true on aws_cognito_identity_pool and unauthenticated users can still obtain temporary AWS credentials, bypassing the authentication you just configured. Set it to false. The default in Terraform is false, but it's worth verifying explicitly in code rather than relying on the default.

Audit evidence

AWS Config rule evaluation results showing each Elasticsearch or OpenSearch domain as COMPLIANT confirm Cognito authentication is active. Console screenshots of the domain security configuration page, with a linked User Pool and Identity Pool visible, provide direct visual proof. CloudTrail logs for UpdateDomainConfig or CreateDomain calls should show CognitoOptions.Enabled: true. If the organization runs a compliance scanner, the report should list this control as passing for every domain in scope.

Framework-specific interpretation

Tool mappings

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

  • Compliance.tf Control: es_domain_cognito_authentication_enabled

  • Powerpipe Controls: aws_compliance.control.es_domain_cognito_authentication_enabled, aws_compliance.control.opensearch_domain_cognito_authentication_enabled_for_kibana

  • Prowler Check: opensearch_service_domains_use_cognito_authentication_for_kibana

Last reviewed: 2026-03-09