Como qualquer filme que se preze, há sempre uma reviravolta na história, certo?

Pouco depois de lançar suporte para vários domínios pediram-me que redireccionasse mais alguns domínios secundários.

De acordo com meu último post, deve ser fácil, certo?

caaaalma!! adicionei os novos domínios à lista

  alternative_domains = [
    {
        domain = "alternative.com"
        zone   = "alternative.com"
    },
    {   domain = "www.alternative.com"
        zone   = "alternative.com"
    },
    {   domain = "alternative.se"
        zone   = "alternative.se"
    },
    {   domain = "www.alternative.se"
        zone   = "alternative.se"
    },
    {   domain = "alternative.site"
        zone   = "alternative.site"
    },
    {   domain = "www.alternative.site"
        zone   = "alternative.site"
    }
  ]

Terraform apply … e … rufem os tambores …

Error: Error modifying LB Listener Rule: ValidationError: A rule can only have '5' condition values
	status code: 400, request id: 19c18fa3-ed0d-4ca3-8b3e-d10b5221ac77
dooh!

O problema:

Um “balanceador de carga” pode ter até 100 regras de escuta e cada regra pode ter até 5 condições.

Ao adicionar todos os domínios alternativos à mesma regra, facilmente excedi esse assim que adicionei mais domínios.

resource "aws_alb_listener_rule" "redirect_alternative_domains" {
  count = length(var.alternative_domains) > 0 ? 1 : 0
  listener_arn = aws_alb_listener.listener_443.arn
  priority     = 97
  action {
    type             = "redirect"
    target_group_arn = aws_alb_target_group.dashboard2_target_group.id
    redirect {
      host        = local.env_domain
      status_code = "HTTP_302"
    }
  }
  condition {
    host_header {
      values = [for ad in var.alternative_domains : ad.domain]
    }
  }
}

Um bocado no quadro branco e pensei em duas soluções.

O caminho mais fácil:

Criar uma regra com apenas uma condição para cada reencaminhamento. O que significa que www.domain.com pertenceria a uma regra e domain.com a outra, e por ai fora até atingir o limite de 100 regras.

A solução ideal:

Uma regra por cada zona de DNS distinta e cada regra contem as condições para essa zona específica.

Ou seja, domínio.com e www.domínio.com serão 2 condições da mesma regra.

Arregacei as mangas, criei uma nova variável

distinct_ad_zones   = distinct([for d in var.alternative_domains : d.zone]) 

que serve como condição para determinar quantas regras são criadas.

count = length(var.alternative_domains) > 0 ? length(local.distinct_ad_zones) : 0

E alterei os a condição para procurar os valores no mapa domínio => zona de acordo com a zona a que se destina a regra a ser criada.

condition {
  host_header {
    values = [lookup(local.domain_to_zone_map, local.distinct_ad_zones[count.index])]
}

Aqui é que as coisas se tornaram complicadas!

Para ser completamente honesto, já nem me consigo lembrar se esta foi a primeira condição que testei ou não. A verdade é que não funcionou como esperava!

Esperava que pesquisa também funcionasse ao contrário ( domínio <= zona ), mas a função apenas me estava a devolver um dos valores e não os dois, sendo que, criei outro mapa:

zone_to_domain_map  = zipmap(local.all_zones, local.all_domains)

O que me deixou com dois mapas, um Domínio => Zona e outro zona => Domínio

{
domain.com      = domain.com,
www.domain.com  = domain.com,
domain.xyz      = domain.xyz,
www.domain.xyz  = domain.xyz,
domain.io       = domain.io,
www.domain.io   = domain.io
}

e

{
domain.com = domain.com,
domain.com = www.domain.com,
domain.xyz = domain.xyz,
domain.xyz = www.domain.xyz,
domain.io = domain.io,
domain.io = www.domain.io
}

Esperava também que a função de de lookup() retornasse todos os valores para qualquer chave com o mesmo nome, (local.distinct_ad_zones[count.index]) por exemplo, para zona domain.com obter ambos os domínios, domínio.com e www.domain.com, o que não foi o caso! Por mais voltas que desse, só conseguia obter um domínio por regra ou todos os domínios em todas as regras, mas nunca um par por regra!

No entanto reparei que, cada cada mapa me devolvia valores diferentes, o que resultou numa solução meio manhosa, mas funcional!

resource "aws_alb_listener_rule" "redirect_alternative_domains" {
  count = length(var.alternative_domains) > 0 ? length(local.distinct_ad_zones) : 0

  listener_arn = aws_alb_listener.listener_443.arn

  action {
    type             = "redirect"
    target_group_arn = aws_alb_target_group.dashboard2_target_group.id

    redirect {
      host        = local.env_domain
      status_code = "HTTP_302"
    }
  }

  condition {
    host_header {
      values = [
        lookup(local.domain_to_zone_map, local.distinct_ad_zones[count.index]),
        lookup(local.zone_to_domain_map, local.distinct_ad_zones[count.index])
      ]
    }
  }
}

Conseguem perceber o que fiz? Sim … Simplesmente coloquei os dois mapas na mesma lista, e cada um devolve exactamente os valores que quero, onde quero!

Tenho a certeza que esta não é a solução mais correta e que qualquer dia vai voltar para me chatear, até la, espero não ter que adicionar um terceiro domínio na mesma zona! 🙈