OpenSearch domains should have Cognito authentication enabled for Kibana
Without Cognito authentication, Kibana dashboards are protected only by network-level controls such as VPC placement or IP-based access policies. Those controls carry no user identity, meaning anyone with network access can query indices, view sensitive data, or modify visualizations with no individual accountability.
Cognito adds identity federation, MFA support, and per-user audit trails. You can also integrate external identity providers (SAML, OIDC) through a Cognito User Pool, which gives you centralized control over who can reach the dashboards and a record of who did.
Retrofit consideration
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 = "registry.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"
}This control is enforced automatically with Compliance.tf modules. Start free trial
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"
cognito_options = {
enabled = true
}
}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
}
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"]
}
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 aws_opensearch_domain resource must include a cognito_options block with enabled = true. The block requires three additional arguments: user_pool_id, identity_pool_id, and role_arn, where role_arn points to an IAM role that grants OpenSearch permission to configure Cognito. A domain missing the cognito_options block, or with enabled = false, fails this control. The referenced IAM role must have the AmazonOpenSearchServiceCognitoAccess managed policy attached. Because the Cognito User Pool and Identity Pool must exist before the domain is created or updated, you typically manage them as separate aws_cognito_user_pool and aws_cognito_identity_pool resources.
Common pitfalls
IAM role trust policy misconfiguration
The IAM role passed to role_arn in cognito_options must have a trust policy allowing es.amazonaws.com to assume it. If the trust relationship references only cognito-identity.amazonaws.com, OpenSearch cannot configure the Cognito integration and the domain update will fail. This is the most common reason the cognito_options block applies cleanly in Terraform but the domain never actually enables the feature.
Identity Pool authenticated role mismatch
The Cognito Identity Pool must have its authenticated role configured to allow access to the OpenSearch domain. If aws_cognito_identity_pool_roles_attachment maps the authenticated role to an IAM role without OpenSearch permissions, users will authenticate successfully through Cognito and then hit 403 errors on the dashboard. Authentication passing and authorization failing are easy to conflate during testing.
Cognito options removed on unrelated domain updates
Accidentally dropping the cognito_options block during a refactor disables Cognito authentication on the next terraform apply. Terraform will show the change in the plan, but it is easy to miss when the diff is buried in a larger update. Add lifecycle preconditions or a policy-as-code check to catch this before it reaches production.
Region availability of Cognito
Cognito User Pools are not available in every AWS region or partition. If you deploy OpenSearch domains in a region or partition where Cognito support is absent, this control cannot be satisfied. Verify current regional availability for both services before committing to an architecture that depends on this combination.
Audit evidence
AWS Config rule results showing each domain as compliant, or aws opensearch describe-domain --domain-name <name> output with CognitoOptions.Enabled: true and populated UserPoolId, IdentityPoolId, and RoleArn fields. CloudTrail UpdateDomainConfig or CreateDomain events confirm when Cognito was enabled and by whom.
The Cognito User Pool configuration showing password policies and MFA settings works as supporting evidence for frameworks that require authentication strength documentation.
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
- Compliance.tf Control:
opensearch_domain_cognito_authentication_enabled_for_kibana - 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