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_arntrustscognito-identity.amazonaws.combut notes.amazonaws.com. The role needs an explicitsts:AssumeRoletrust policy fores.amazonaws.com. If you're creating it withaws_iam_role, verify theassume_role_policydocument before runningterraform applyor the apply will fail mid-update.Cognito pool region mismatch
user_pool_idandidentity_pool_idmust 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_domaintoaws_opensearch_domain, carry thecognito_optionsblock over explicitly. Terraform import andstate mvoperations don't validate that configuration arguments survived the move, so it's easy to end up with a domain that passesterraform planbut fails this control.Identity Pool unauthenticated access left enabled
Leave
allow_unauthenticated_identities = trueonaws_cognito_identity_pooland unauthenticated users can still obtain temporary AWS credentials, bypassing the authentication you just configured. Set it tofalse. The default in Terraform isfalse, 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
Related controls
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
Compliance.tf Control:
es_domain_cognito_authentication_enabledPowerpipe Controls:
aws_compliance.control.es_domain_cognito_authentication_enabled,aws_compliance.control.opensearch_domain_cognito_authentication_enabled_for_kibanaProwler Check:
opensearch_service_domains_use_cognito_authentication_for_kibana
Last reviewed: 2026-03-09