Hello,
I created a module to create a Cognito User Pool with a AWS domain configured :
################################################################################
# Cognito User Pool
################################################################################
resource "aws_cognito_user_pool" "this" {
# User Pool name
name = local.user_pool_name
# Basic configuration
auto_verified_attributes = var.auto_verified_attributes
deletion_protection = var.deletion_protection
mfa_configuration = var.mfa_configuration
user_pool_tier = var.user_pool_tier
# Email and SMS configuration
email_verification_message = var.email_verification_message
email_verification_subject = var.email_verification_subject
sms_authentication_message = var.sms_authentication_message
sms_verification_message = var.sms_verification_message
# Users configuration
username_attributes = var.username_attributes
username_configuration {
case_sensitive = var.username_configuration.case_sensitive
}
# Password policy configuration
password_policy {
minimum_length = var.password_policy.minimum_length
password_history_size = var.password_policy.password_history_size
require_lowercase = var.password_policy.require_lowercase
require_numbers = var.password_policy.require_numbers
require_symbols = var.password_policy.require_symbols
require_uppercase = var.password_policy.require_uppercase
temporary_password_validity_days = var.password_policy.temporary_password_validity_days
}
# Connection policy configuration
sign_in_policy {
allowed_first_auth_factors = var.sign_in_policy.allowed_first_auth_factors
}
# Account recover policy configuration
dynamic "account_recovery_setting" {
for_each = var.account_recovery_setting != null ? [var.account_recovery_setting] : []
content {
dynamic "recovery_mechanism" {
for_each = account_recovery_setting.value.recovery_mechanism
content {
name = recovery_mechanism.value.name
priority = recovery_mechanism.value.priority
}
}
}
}
# Administration configuration
admin_create_user_config {
allow_admin_create_user_only = var.admin_create_user_config.allow_admin_create_user_only
}
# Email configuration
email_configuration {
configuration_set = var.email_configuration.configuration_set
email_sending_account = var.email_configuration.email_sending_account
from_email_address = var.email_configuration.from_email_address
reply_to_email_address = var.email_configuration.reply_to_email_address
source_arn = var.email_configuration.source_arn
}
# Message template
verification_message_template {
default_email_option = var.verification_message_template.default_email_option
email_message = var.verification_message_template.email_message
email_message_by_link = var.verification_message_template.email_message_by_link
email_subject = var.verification_message_template.email_subject
email_subject_by_link = var.verification_message_template.email_subject_by_link
sms_message = var.verification_message_template.sms_message
}
# Tags
tags = var.tags
}
################################################################################
# Cognito Resource Server
################################################################################
# moved {
# from = aws_cognito_resource_server.this
# to = aws_cognito_resource_server.this["ivalua-manual-poc"]
# }
resource "aws_cognito_resource_server" "this" {
# for_each = { for idx, server in var.cognito_resource_servers : server.identifier => server }
for_each = var.cognito_resource_servers
user_pool_id = aws_cognito_user_pool.this.id
identifier = each.value.identifier
name = each.value.identifier
dynamic "scope" {
for_each = each.value.scopes
content {
scope_description = scope.value.scope_description
scope_name = scope.value.scope_name
}
}
}
################################################################################
# Cognito App Client = machine-to-machine user
################################################################################
resource "aws_cognito_user_pool_client" "this" {
for_each = { for idx, client in local.merged_clients : client.name => client }
user_pool_id = aws_cognito_user_pool.this.id
name = each.value.name
access_token_validity = each.value.access_token_validity
allowed_oauth_flows = each.value.allowed_oauth_flows
allowed_oauth_flows_user_pool_client = each.value.allowed_oauth_flows_user_pool_client
allowed_oauth_scopes = each.value.allowed_oauth_scopes
auth_session_validity = each.value.auth_session_validity
callback_urls = each.value.callback_urls
default_redirect_uri = each.value.default_redirect_uri
enable_propagate_additional_user_context_data = each.value.enable_propagate_additional_user_context_data
enable_token_revocation = each.value.enable_token_revocation
explicit_auth_flows = each.value.explicit_auth_flows
id_token_validity = each.value.id_token_validity
logout_urls = each.value.logout_urls
prevent_user_existence_errors = each.value.prevent_user_existence_errors
read_attributes = each.value.read_attributes
refresh_token_validity = each.value.refresh_token_validity
supported_identity_providers = each.value.supported_identity_providers
write_attributes = each.value.write_attributes
# To provide secret, to avoid "client_credentials flow can not be selected if client does not have a client secret." error
generate_secret = true
token_validity_units {
access_token = try(each.value.token_validity_units.access_token, "minutes")
id_token = try(each.value.token_validity_units.id_token, "minutes")
refresh_token = try(each.value.token_validity_units.refresh_token, "days")
}
depends_on = [
aws_cognito_resource_server.this
]
}
################################################################################
# Cognito Comain
################################################################################
resource "aws_cognito_user_pool_domain" "this" {
# Remove the `_` from the ID and remove capital character
# ex: eu-west-3_XXXXX -> eu-west-3xxxxx
domain = lower(replace(aws_cognito_user_pool.this.id, "/_/", ""))
user_pool_id = aws_cognito_user_pool.this.id
managed_login_version = 2
}
When I apply for the first time, it does create evrything :
Terraform will perform the following actions:
# module.cognito2.aws_cognito_resource_server.this["ivalua-manual-poc"] will be created
+ resource "aws_cognito_resource_server" "this" {
+ id = (known after apply)
+ identifier = "manual-poc"
+ name = "manual-poc"
+ region = "eu-west-3"
+ scope_identifiers = (known after apply)
+ user_pool_id = (known after apply)
+ scope {
+ scope_description = "application 1"
+ scope_name = "app1-read"
}
+ scope {
+ scope_description = "read/write"
+ scope_name = "app1-rw"
}
}
# module.cognito2.aws_cognito_user_pool.this will be created
+ resource "aws_cognito_user_pool" "this" {
+ arn = (known after apply)
+ creation_date = (known after apply)
+ custom_domain = (known after apply)
+ deletion_protection = "ACTIVE"
+ domain = (known after apply)
+ email_verification_message = (known after apply)
+ email_verification_subject = (known after apply)
+ endpoint = (known after apply)
+ estimated_number_of_users = (known after apply)
+ id = (known after apply)
+ last_modified_date = (known after apply)
+ mfa_configuration = "OFF"
+ name = "test-fla-userpool-1"
+ region = "eu-west-3"
+ sms_verification_message = (known after apply)
+ tags_all = (known after apply)
+ user_pool_tier = "ESSENTIALS"
+ account_recovery_setting {
+ recovery_mechanism {
+ name = "verified_email"
+ priority = 1
}
+ recovery_mechanism {
+ name = "verified_phone_number"
+ priority = 2
}
}
+ admin_create_user_config {
+ allow_admin_create_user_only = true
}
+ email_configuration {
+ email_sending_account = "COGNITO_DEFAULT"
}
+ password_policy {
+ minimum_length = 8
+ password_history_size = 0
+ require_lowercase = true
+ require_numbers = true
+ require_symbols = true
+ require_uppercase = true
+ temporary_password_validity_days = 7
}
+ sign_in_policy {
+ allowed_first_auth_factors = [
+ "PASSWORD",
]
}
+ sms_configuration (known after apply)
+ username_configuration {
+ case_sensitive = false
}
+ verification_message_template {
+ default_email_option = "CONFIRM_WITH_CODE"
+ email_message = (known after apply)
+ email_message_by_link = (known after apply)
+ email_subject = (known after apply)
+ email_subject_by_link = (known after apply)
+ sms_message = (known after apply)
}
}
# module.cognito2.aws_cognito_user_pool_client.this["client 1"] will be created
+ resource "aws_cognito_user_pool_client" "this" {
+ access_token_validity = 60
+ allowed_oauth_flows = [
+ "client_credentials",
]
+ allowed_oauth_flows_user_pool_client = true
+ allowed_oauth_scopes = [
+ "manual-poc/app1-read",
]
+ auth_session_validity = 3
+ callback_urls = []
+ client_secret = (sensitive value)
+ default_redirect_uri = (known after apply)
+ enable_propagate_additional_user_context_data = false
+ enable_token_revocation = true
+ explicit_auth_flows = [
+ "ALLOW_REFRESH_TOKEN_AUTH",
]
+ generate_secret = true
+ id = (known after apply)
+ id_token_validity = 60
+ logout_urls = []
+ name = "client 1"
+ prevent_user_existence_errors = (known after apply)
+ read_attributes = []
+ refresh_token_validity = 5
+ region = "eu-west-3"
+ supported_identity_providers = [
+ "COGNITO",
]
+ user_pool_id = (known after apply)
+ write_attributes = []
+ token_validity_units {
+ access_token = "minutes"
+ id_token = "minutes"
+ refresh_token = "days"
}
}
# module.cognito2.aws_cognito_user_pool_domain.this will be created
+ resource "aws_cognito_user_pool_domain" "this" {
+ aws_account_id = (known after apply)
+ cloudfront_distribution = (known after apply)
+ cloudfront_distribution_arn = (known after apply)
+ cloudfront_distribution_zone_id = (known after apply)
+ domain = (known after apply)
+ id = (known after apply)
+ managed_login_version = 2
+ region = "eu-west-3"
+ s3_bucket = (known after apply)
+ user_pool_id = (known after apply)
+ version = (known after apply)
}
Plan: 4 to add, 0 to change, 0 to destroy.
Changes to Outputs:
~ cognito2_clients = (sensitive value)
~ cognito2_user_pool_arn = "arn:aws:cognito-idp:eu-west-3:710412343115:userpool/eu-west-3_4FAt0B9fD" -> (known after apply)
~ cognito2_user_pool_domain = "eu-west-34fat0b9fd" -> (known after apply)
~ cognito2_user_pool_endpoint = "cognito-idp.eu-west-3.amazonaws.com/eu-west-3_4FAt0B9fD" -> (known after apply)
~ cognito2_user_pool_id = "eu-west-3_4FAt0B9fD" -> (known after apply)
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
module.cognito2.aws_cognito_user_pool.this: Creating...
module.cognito2.aws_cognito_user_pool.this: Creation complete after 1s [id=eu-west-3_iVxLi4E5u]
module.cognito2.aws_cognito_user_pool_domain.this: Creating...
module.cognito2.aws_cognito_resource_server.this["ivalua-manual-poc"]: Creating...
module.cognito2.aws_cognito_resource_server.this["ivalua-manual-poc"]: Creation complete after 1s [id=eu-west-3_iVxLi4E5u|manual-poc]
module.cognito2.aws_cognito_user_pool_client.this["client 1"]: Creating...
module.cognito2.aws_cognito_user_pool_client.this["client 1"]: Creation complete after 0s [id=4bpvvf6r44mqce8jrpngmrh6c1]
module.cognito2.aws_cognito_user_pool_domain.this: Creation complete after 1s [id=eu-west-3ivxli4e5u]
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
Outputs:
cognito2_clients = <sensitive>
cognito2_user_pool_arn = "arn:aws:cognito-idp:eu-west-3:710412343115:userpool/eu-west-3_iVxLi4E5u"
cognito2_user_pool_domain = ""
cognito2_user_pool_endpoint = "cognito-idp.eu-west-3.amazonaws.com/eu-west-3_iVxLi4E5u"
cognito2_user_pool_id = "eu-west-3_iVxLi4E5u"
cognito2_user_pool_name = "test-fla-userpool-1"
Like you see, the `domain` is empty. If I reapply, here’s the output :
Note: Objects have changed outside of Terraform
Terraform detected the following changes made outside of Terraform since the last "terraform apply" which may have affected this plan:
# module.cognito2.aws_cognito_user_pool.this has changed
~ resource "aws_cognito_user_pool" "this" {
+ domain = "eu-west-3ivxli4e5u"
id = "eu-west-3_iVxLi4E5u"
name = "test-fla-userpool-1"
# (15 unchanged attributes hidden)
# (7 unchanged blocks hidden)
}
Unless you have made equivalent changes to your configuration, or ignored the relevant attributes using ignore_changes, the following plan may include actions to undo or respond to these changes.
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Changes to Outputs:
~ cognito2_user_pool_domain = "" -> "eu-west-3ivxli4e5u"
You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value:
How to fix this behavior ?