Two apply needed to up-to-date Cognito resource

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 ?