Why does my user_pool_domain always need to be replaced?

I get the following whenever I run terraform plan/apply and I don’t know why this is saying it always needs to be replaced. The ACM is managed at the root of my project, and then the ARN is passed to my Cognito module.

# module.cognito["users"].aws_cognito_user_pool_domain.main must be replaced
+/- resource "aws_cognito_user_pool_domain" "main" {
      ~ certificate_arn             = "arn:aws:acm:us-east-1:123456789:certificate/bc955b8a-45c6-4003-1b2a-5z66333fef275" -> (known after apply) # forces replacement

cognito.tf (root of the project)

module "cognito" {
  source = "../modules/cognito"

  for_each = var.cognito_userpools

  cognito_name_prefix                             = "${try(each.value.name_prefix, local.name_prefix)}-${each.key}"
  cognito_domain_name                             = try("${each.value.domain_prefix}.${local.dns_address}", null)
  cognito_https_acm_arn                           = try(aws_acm_certificate.cognito_https_cert[each.key].arn, null)
  hosted_zone_id                                  = try(aws_route53_zone.public_hosted_zone.id, null)
  cognito_callback_urls                           = each.value.callback_urls
  cognito_logout_urls                             = each.value.logout_urls
  cognito_sms_external_id                         = each.value.sms_external_id
  cognito_userpool_schemas                        = each.value.userpool_schemas
  cognito_mfa_configuration                       = try(each.value.mfa_configuration, "ON")
  cognito_enable_software_token_mfa_configuration = try(each.value.enable_software_token_mfa_configuration, true)
  cognito_userpool_groups                         = try(each.value.groups, [])
  tags                                            = local.default_tags

acm.tf (root of the project)

resource "aws_acm_certificate" "cognito_https_cert" {
  for_each                  = var.cognito_userpools
  provider                  = aws.us-east-1
  domain_name               = "${each.value.domain_prefix}.${local.dns_address}"
  subject_alternative_names = ["*.${each.value.domain_prefix}.${local.dns_address}"]

  validation_method = "DNS"

  tags = local.default_tags

  lifecycle {
    create_before_destroy = true


resource "aws_cognito_user_pool_domain" "main" {
  domain          = var.cognito_domain_name
  certificate_arn = var.cognito_https_acm_arn
  user_pool_id    = aws_cognito_user_pool.pool.id

resource "aws_route53_record" "cognito_record" {
  name            = aws_cognito_user_pool_domain.main.domain
  type            = "A"
  zone_id         = var.hosted_zone_id
  allow_overwrite = true

  alias {
    evaluate_target_health = false
    name                   = aws_cognito_user_pool_domain.main.cloudfront_distribution_arn
    # NOTE: This zone_id is fixed
    zone_id = "Z2FDTNDATAQYW2"

The ACM certificate is not being updated in the plan/not showing anything obvious as to why this module in particular thinks it needs to replace the Cognito domain. I use a similar setup in a separate module for APIs and pass the ACM certificate ARN in the same way without issues.

Anyone have any ideas?


Upon further investigation tag updates were causing the arn to not be known until after apply, this isn’t ideal and only a problem with cognito custom domains - I don’t have the same issue with API Gateway custom domains for example. Does anyone have any idea on a workaround rather than relying on ignoring tag updates as a lifecycle meta argument.